签入版本

This commit is contained in:
2026-10-08 17:50:29 +08:00
parent 809750b47e
commit e213a9b31d
444 changed files with 39366 additions and 37533 deletions
@@ -1,4 +1,4 @@
# 2.2 后端服务启动
# 2.3 后端服务启动
:::warning 特别提醒
官方精心制作本教程,目的在于方便用户快速掌握软件产品的使用和部署。通过此教程,刚入行的开发者也可以快速掌握并投入产品研发。本地部署时务必请耐心阅读文档操作。
@@ -11,19 +11,19 @@
```text
1. 登录[官方网站](https://www.rxthink.cn),进入「个人中心」→「我的订单」页面。
2. 在订单列表中找到已购买的授权订单,点击「下载」按钮。
3. 下载的压缩包包含完整的前后端源码、数据库脚本及部署配置文件。
4. 将压缩包解压到本地开发目录(如 D:\xampp8.2\htdocs\ 或 ~/www/)。
3. 下载的压缩包包含完整的后端源码、数据库脚本及部署配置文件。
4. 将压缩包解压到本地开发目录(如 D:\laragon\www\ 或 ~/www/)。
```
```bash
# 进入项目目录(以实际解压路径为准)
cd thinkphp6
cd RXThinkCMF_Laravel13_EleVue
```
:::tip 温馨提示
1. 源码包请务必从[官方网站](https://www.rxthink.cn)订单中心下载,确保获取的是正版授权的最新版本。
2. 授权有效期内可无限次下载最新版本,版本更新后可重新下载获取最新源码。
3. 解压后请先阅读根目录下的 `README.md` 和 `CHANGELOG.md` 了解版本更新内容。
3. 解压后请先阅读根目录下的 `README.md` 了解项目概况与技术栈。
:::
## 步骤二:安装 Composer 依赖
@@ -47,88 +47,123 @@ composer install
安装完成后,项目根目录会生成 `vendor/` 目录,包含所有第三方依赖。
:::tip 一键初始化
以上两步(含后续的环境文件、APP_KEY、数据库迁移、前端构建)可合并为一条命令:
```bash
composer setup
# 等价于:composer install → 复制 .env → key:generate → migrate --force → npm install → npm run build
```
:::
## 步骤三:配置环境文件
```bash
# 复制环境配置模板(.env.example 是模板文件,.env 是实际配置)
cp .env.example .env
# 生成应用加密密钥(APP_KEY,写入 .env)
php artisan key:generate
```
编辑 `.env` 文件,配置数据库连接等信息:
```ini
; 开启调试模式(开发环境 true,生产环境必须 false)
APP_DEBUG = true
# 开启调试模式(开发环境 true,生产环境必须 false)
APP_DEBUG=true
; 数据库连接配置
[DATABASE]
TYPE = mysql ; 数据库类型(mysql / pgsql / sqlsrv / oracle / sqlite)
HOSTNAME = 127.0.0.1 ; 数据库服务器地址
DATABASE = rxthinkcmf.thinkphp6.elevue ; 数据库名称
USERNAME = root ; 数据库用户名
PASSWORD = root ; 数据库密码
HOSTPORT = 3306 ; 数据库端口
PREFIX = think_ ; 表前缀
# 数据库连接配置
DB_CONNECTION=mysql # 数据库类型(mysql / pgsql / sqlsrv / sqlite)
DB_HOST=127.0.0.1 # 数据库服务器地址
DB_PORT=3306 # 数据库端口
DB_DATABASE=rxthinkcmf.laravel13.elevue # 数据库名称(需提前创建)
DB_USERNAME=root # 数据库用户名
DB_PASSWORD=root # 数据库密码
DB_PREFIX=laravel_ # 表前缀(数据库脚本使用 laravel_ 前缀,必须保留)
; JWT 签名密钥(生产环境务必修改为强随机字符串)
[JWT]
SECRET = rxthinkcmf_jwt_secret_key_2024
# JWT 签名密钥(必须 >= 32 字符,应用启动 fail-close 校验)
JWT_SECRET=your_random_jwt_secret_key_at_least_32_bytes
; 文件上传配置
[FILE]
UPLOAD_DIR = D:/uploads/rxthinkcmf ; 上传文件存储目录(绝对路径)
DOMAIN_URL = http://file.thinkphp6.elevue ; 文件访问域名
# Redis 配置(必需;设置了 requirepass 时必须同步)
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
# 会话 / 缓存 / 队列统一走 Redis
SESSION_DRIVER=redis
CACHE_STORE=redis
QUEUE_CONNECTION=redis
# 文件上传配置
FILE_UPLOAD_DIR=uploads # 上传目录(相对 public/)
FILE_DOMAIN_URL= # 文件访问域名(空则返回相对路径)
```
:::danger 生产环境务必修改
1. `[JWT]` 下的 `SECRET` 必须修改为强随机字符串,生成方式:`php -r "echo bin2hex(random_bytes(32));"`
2. `APP_DEBUG` 生产环境必须设为 `false`
3. `[DATABASE]` 下的 `PASSWORD` 必须修改为强密码
1. `JWT_SECRET` 必须修改为强随机字符串(>= 32 字符),生成方式:`openssl rand -base64 32`。为空或长度不足时应用**拒绝启动**。
2. `APP_DEBUG` 生产环境必须设为 `false`。
3. `DB_PASSWORD` 必须修改为强密码。
:::
## 步骤四:启动内置开发服务器
ThinkPHP 内置了 PHP 开发服务器,无需安装 Nginx/Apache 即可启动:
## 步骤四:启动服务
```bash
# -----------------------------------------------------------------------------
# 启动开发服务器(默认监听 127.0.0.1:8000)
# 推荐:一键启动(同时启动 API 服务 + 队列 + 日志流 + Vite 四个进程)
# -----------------------------------------------------------------------------
# run → 启动 ThinkPHP 内置的 PHP 开发服务器
composer dev
# -----------------------------------------------------------------------------
# 方式二:单独启动 API 服务(内置 PHP 开发服务器,默认监听 127.0.0.1:8000)
# -----------------------------------------------------------------------------
# serve → 启动 Laravel 内置的 PHP 开发服务器
# 启动后浏览器访问 http://localhost:8000 即可
php think run
php artisan serve
# -----------------------------------------------------------------------------
# 指定端口(当 8000 被占用时更换端口)
# -----------------------------------------------------------------------------
# --port → 指定监听端口
php think run --port 8080
php artisan serve --port=8080
# -----------------------------------------------------------------------------
# 指定主机和端口(允许局域网其他设备访问)
# -----------------------------------------------------------------------------
# --host 0.0.0.0 → 监听所有网络接口(默认只监听 127.0.0.1)
# --host=0.0.0.0 → 监听所有网络接口(默认只监听 127.0.0.1)
# 适用于手机调试、前后端分离开发等场景
php think run --host 0.0.0.0 --port 8000
php artisan serve --host=0.0.0.0 --port=8000
```
启动成功后会看到:
```html
ThinkPHP Development server is started
<http://0.0.0.0:8000>
You can exit with `CTRL-C`
```text
INFO Server running on [http://127.0.0.1:8000].
INFO Press Ctrl-C to quit.
```
:::tip composer dev 四进程说明
| 进程 | 命令 | 用途 |
|------|------|------|
| server | `php artisan serve` | API 服务 |
| queue | `php artisan queue:listen` | 队列消费(导入导出等异步任务) |
| logs | `php artisan pail` | 实时日志流 |
| vite | `npm run dev` | 前端构建(HMR) |
:::
## 步骤五:验证服务
```bash
# -----------------------------------------------------------------------------
# 访问根路径,确认服务正常运行
# 健康检查(Laravel 内置 /up 路由)
# -----------------------------------------------------------------------------
# curl → 命令行 HTTP 客户端工具
# 正常响应:{"code":200,"msg":"RxThinkCMF API Server","data":{"version":"1.0.0"}}
curl http://localhost:8000/
curl http://localhost:8000/up
# → {"status":"ok"}
# -----------------------------------------------------------------------------
# 验证码接口(公开接口,同时验证 PHP → Redis 链路)
# -----------------------------------------------------------------------------
curl http://localhost:8000/api/captcha
# → {"code":0,"ok":true,"msg":"操作成功","data":{"key":"...","captcha":"data:image/png;base64,..."}}
# -----------------------------------------------------------------------------
# 测试登录接口(POST 请求 + JSON 请求体)
@@ -140,10 +175,8 @@ curl -X POST http://localhost:8000/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"123456"}'
# 测试租户账号登录
curl -X POST http://localhost:8000/api/login \
-H "Content-Type: application/json" \
-d '{"username":"tenant001","password":"123456"}'
# 未传验证码时返回校验失败响应(说明路由与验证链路已通):
# {"code":1,"ok":false,"msg":"请输入验证码","data":null}
```
:::tip 内置演示账号
@@ -151,6 +184,8 @@ curl -X POST http://localhost:8000/api/login \
|------|------|------|------|
| admin | 123456 | 超级管理员 | 平台管理,可管理所有租户 |
| tenant001 | 123456 | 租户管理员(客户租户A) | 租户视角,体验数据隔离效果 |
登录成功后请立即修改默认密码。
:::
## 常见问题
@@ -169,18 +204,29 @@ php -d memory_limit=-1 /usr/local/bin/composer install
chmod -R 755 vendor/
```
### 3. 数据库连接失败
### 3. 应用拒绝启动,报 JWT_SECRET 错误
`.env` 中 `JWT_SECRET` 为空或不足 32 字符(应用启动 fail-close 校验)。生成并重试:
```bash
openssl rand -base64 32 # 或 php -r "echo bin2hex(random_bytes(32));"
# 将结果写入 .env 的 JWT_SECRET 后重新启动
```
### 4. 数据库连接失败
检查 `.env` 中的数据库配置是否正确:
```bash
# 1. 检查 .env 中 [DATABASE] 配置:HOSTNAME、PORT、USERNAME、PASSWORD、DATABASE 是否正确
# 2. 确认数据库服务已启动
# 1. 检查 .env 中 DB_* 配置:DB_HOST、DB_PORT、DB_USERNAME、DB_PASSWORD、DB_DATABASE 是否正确
# 2. 确认数据库服务已启动、数据库已创建并导入脚本
# 3. 用原生 SQL 测试连接是否正常(以 MySQL 为例)
mysql -h 127.0.0.1 -P 3306 -u root -p -e "SELECT 1;"
# 4. 若报表不存在,检查 DB_PREFIX 是否为 laravel_
```
### 4. 端口被占用
### 5. 端口被占用
```bash
# Windows:查找占用 8000 端口的进程
@@ -188,9 +234,20 @@ mysql -h 127.0.0.1 -P 3306 -u root -p -e "SELECT 1;"
netstat -ano | findstr :8000
# macOS / Linux:查找占用 8000 端口的进程
# -i :8000 → 只显示使用 8000 端口的进程
lsof -i :8000
# 更换端口启动(避免冲突)
php think run --port 8080
php artisan serve --port=8080
```
### 6. 验证码接口报"验证码服务不可用"
Redis 未启动或密码不匹配(`.env` 中 `REDIS_PASSWORD` 与 Redis 实际 `requirepass` 不一致)。验证:
```bash
redis-cli ping # 输出 PONG
php artisan config:clear # 清除配置缓存
php artisan tinker --execute="try { \Illuminate\Support\Facades\Redis::setex('t', 5, 'ok'); echo 'OK'; } catch (\Throwable \$e) { echo \$e->getMessage(); }"
```
详见 [2.2 Laragon 环境搭建](/zh/quickstart/laragon.html) 「Redis 密码配置」。
@@ -1,7 +1,7 @@
# 2.4 数据库初始化
# 2.5 数据库初始化
:::tip 概述
本章指导您完成数据库的创建和 SQL 脚本导入。项目为每种数据库提供了独立的 SQL 脚本,存放在 `document/` 目录下按数据库类型分组。
本章指导您完成数据库的创建和 SQL 脚本导入。项目为每种数据库提供了独立的 SQL 脚本,存放在 `document/` 目录下按数据库类型分组,可直接导入使用,无需手动转换。
:::
## SQL 脚本目录结构
@@ -9,18 +9,22 @@
```text
document/
├── mysql/
│ └── rxthinkcmf.thinkphp6.elevue.sql # MySQL 版本
│ └── rxthinkcmf.laravel13.elevue.sql # MySQL 版本
├── postgresql/
│ └── rxthinkcmf.thinkphp6.elevue.sql # PostgreSQL 版本
│ └── rxthinkcmf.laravel13.elevue.sql # PostgreSQL 版本
├── sqlserver/
│ └── rxthinkcmf.thinkphp6.elevue.sql # SQL Server 版本
│ └── rxthinkcmf.laravel13.elevue.sql # SQL Server 版本
├── oracle/
│ └── rxthinkcmf.thinkphp6.elevue.sql # Oracle 版本
│ └── rxthinkcmf.laravel13.elevue.sql # Oracle 版本
└── sqlite/
└── rxthinkcmf.thinkphp6.elevue.sql # SQLite 版本
└── rxthinkcmf.laravel13.elevue.sql # SQLite 版本
```
各数据库脚本已针对对应的 SQL 语法做了适配,可直接导入使用,无需手动转换。
各数据库脚本已针对对应的 SQL 语法做了适配,包含完整的表结构(25 张业务表,`laravel_` 前缀)与初始数据。
:::warning 本项目未使用 Laravel 迁移建表
`database/migrations/` 目录为空,`php artisan migrate` 只会初始化迁移记录表,**不会创建业务表**。数据库结构与初始数据以 SQL 脚本为准,表前缀为 `laravel_`(对应 `.env` 中 `DB_PREFIX`)。
:::
---
@@ -37,7 +41,7 @@ mysql -u root -p
-- 创建项目数据库
-- CHARACTER SET utf8mb4 → 支持 emoji 等四字节字符
-- COLLATE utf8mb4_general_ci → 不区分大小写的排序规则
CREATE DATABASE `rxthinkcmf.thinkphp6.elevue` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE DATABASE `rxthinkcmf.laravel13.elevue` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
EXIT;
```
@@ -45,29 +49,29 @@ EXIT;
```bash
# -----------------------------------------------------------------------------
# 导入 MySQL 专用初始化脚本
# 导入 MySQL 专用初始化脚本(约 226MB,耗时 1~3 分钟)
# -----------------------------------------------------------------------------
# -u root -proot → 数据库用户名和密码
# rxthinkcmf... → 目标数据库名
# < file.sql → 将 SQL 文件内容作为 mysql 命令的输入
mysql -u root -proot rxthinkcmf.thinkphp6.elevue < document/mysql/rxthinkcmf.thinkphp6.elevue.sql
mysql -u root -proot rxthinkcmf.laravel13.elevue < document/mysql/rxthinkcmf.laravel13.elevue.sql
```
:::tip 温馨提示
导入完成后,数据库中包含完整的表结构和初始数据(管理员账号、菜单、字典、城市数据等),可直接使用。
- 使用 [2.8 Docker 快速体验](/zh/quickstart/docker.html) 时无需手动导入,MySQL 容器**首次启动自动执行**该脚本;
- Windows PowerShell 不支持 `<` 重定向,可改用 CMD 执行,或在 Navicat / phpMyAdmin 中导入。
:::
### 3. 配置 .env
```ini
[DATABASE]
TYPE = mysql ; 数据库类型
HOSTNAME = 127.0.0.1 ; 数据库地址
DATABASE = rxthinkcmf.thinkphp6.elevue ; 数据库名
USERNAME = root ; 用户名
PASSWORD = root ; 密码
HOSTPORT = 3306 ; 端口
PREFIX = think_ ; 表前缀
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=rxthinkcmf.laravel13.elevue
DB_USERNAME=root
DB_PASSWORD=root
DB_PREFIX=laravel_ # 必须保留,脚本中表均为 laravel_ 前缀
```
---
@@ -82,16 +86,13 @@ sudo -u postgres psql
```
```sql
-- 创建数据库用户(如已存在可跳过)
CREATE USER postgres WITH PASSWORD 'root';
-- 创建项目数据库
-- OWNER postgres → 指定数据库所有者
-- ENCODING 'UTF8' → 使用 UTF-8 编码
CREATE DATABASE "rxthinkcmf.thinkphp6.elevue" OWNER postgres ENCODING 'UTF8';
CREATE DATABASE "rxthinkcmf.laravel13.elevue" OWNER postgres ENCODING 'UTF8';
-- 授予用户对该数据库的所有权限
GRANT ALL PRIVILEGES ON DATABASE "rxthinkcmf.thinkphp6.elevue" TO postgres;
GRANT ALL PRIVILEGES ON DATABASE "rxthinkcmf.laravel13.elevue" TO postgres;
\q
```
@@ -102,22 +103,21 @@ GRANT ALL PRIVILEGES ON DATABASE "rxthinkcmf.thinkphp6.elevue" TO postgres;
# 导入 PostgreSQL 专用初始化脚本
# -----------------------------------------------------------------------------
# -U postgres → 连接用户
# -d ... → 目标数据库
# -d ... → 目标数据库
# -f file.sql → 执行 SQL 文件
psql -U postgres -d rxthinkcmf.thinkphp6.elevue -f document/postgresql/rxthinkcmf.thinkphp6.elevue.sql
psql -U postgres -d rxthinkcmf.laravel13.elevue -f document/postgresql/rxthinkcmf.laravel13.elevue.sql
```
### 3. 配置 .env
```ini
[DATABASE]
TYPE = pgsql ; 数据库类型改为 pgsql
HOSTNAME = 127.0.0.1
DATABASE = rxthinkcmf.thinkphp6.elevue
USERNAME = postgres ; PostgreSQL 默认用户
PASSWORD = root
HOSTPORT = 5432 ; PostgreSQL 默认端口
PREFIX = think_
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=rxthinkcmf.laravel13.elevue
DB_USERNAME=postgres
DB_PASSWORD=root
DB_PREFIX=laravel_
```
---
@@ -128,13 +128,13 @@ PREFIX = think_
```bash
# 安装 PHP 的 SQLite 扩展(如尚未安装)
sudo apt install php8.2-sqlite3
sudo apt install php8.3-sqlite3
# 创建数据库文件存放目录(-p 参数:目录不存在时自动创建)
mkdir -p /www/api/runtime
mkdir -p database
# 创建空的 SQLite 数据库文件(touch 创建空文件)
touch /www/api/runtime/database.sqlite
touch database/database.sqlite
```
### 2. 导入 SQL 脚本
@@ -146,16 +146,15 @@ touch /www/api/runtime/database.sqlite
# sqlite3 → SQLite 命令行工具
# 数据库文件路径 → SQLite 数据库存储在单个文件中
# < file.sql → 将 SQL 文件作为输入执行
sqlite3 /www/api/runtime/database.sqlite < document/sqlite/rxthinkcmf.thinkphp6.elevue.sql
sqlite3 database/database.sqlite < document/sqlite/rxthinkcmf.laravel13.elevue.sql
```
### 3. 配置 .env
```ini
[DATABASE]
TYPE = sqlite ; 数据库类型改为 sqlite
DATABASE = /www/api/runtime/database.sqlite ; SQLite 文件路径
PREFIX = think_
DB_CONNECTION=sqlite
# DB_DATABASE 指向 database/database.sqlite(Laravel 默认值,无需修改)
DB_PREFIX=laravel_
```
:::tip SQLite 特点
@@ -179,7 +178,7 @@ PREFIX = think_
# -P root → 密码
# -Q → 执行 SQL 查询后退出
# COLLATE Chinese_PRC_CI_AS → 中文排序规则(不区分大小写)
sqlcmd -S localhost -U sa -P root -Q "CREATE DATABASE [rxthinkcmf.thinkphp6.elevue] COLLATE Chinese_PRC_CI_AS"
sqlcmd -S localhost -U sa -P root -Q "CREATE DATABASE [rxthinkcmf.laravel13.elevue] COLLATE Chinese_PRC_CI_AS"
```
### 2. 导入 SQL 脚本
@@ -190,20 +189,19 @@ sqlcmd -S localhost -U sa -P root -Q "CREATE DATABASE [rxthinkcmf.thinkphp6.elev
# -----------------------------------------------------------------------------
# -d rxthinkcmf... → 目标数据库
# -i file.sql → 执行 SQL 文件
sqlcmd -S localhost -U sa -P root -d rxthinkcmf.thinkphp6.elevue -i document/sqlserver/rxthinkcmf.thinkphp6.elevue.sql
sqlcmd -S localhost -U sa -P root -d rxthinkcmf.laravel13.elevue -i document/sqlserver/rxthinkcmf.laravel13.elevue.sql
```
### 3. 配置 .env
```ini
[DATABASE]
TYPE = sqlsrv ; 数据库类型改为 sqlsrv
HOSTNAME = 127.0.0.1
DATABASE = rxthinkcmf.thinkphp6.elevue
USERNAME = sa ; SQL Server 默认管理员
PASSWORD = root
HOSTPORT = 1433 ; SQL Server 默认端口
PREFIX = think_
DB_CONNECTION=sqlsrv
DB_HOST=127.0.0.1
DB_PORT=1433
DB_DATABASE=rxthinkcmf.laravel13.elevue
DB_USERNAME=sa
DB_PASSWORD=root
DB_PREFIX=laravel_
```
---
@@ -220,15 +218,15 @@ sqlplus system/root@localhost:1521/XE
-- DATAFILE → 数据文件的物理路径
-- SIZE 100M → 初始大小 100MB
-- AUTOEXTEND ON NEXT 100M → 空间不足时自动扩展 100MB
CREATE TABLESPACE "rxthinkcmf_thinkphp6_elevue"
DATAFILE '/u01/oradata/rxthinkcmf_thinkphp6_elevue.dbf'
CREATE TABLESPACE "rxthinkcmf_laravel13_elevue"
DATAFILE '/u01/oradata/rxthinkcmf_laravel13_elevue.dbf'
SIZE 100M AUTOEXTEND ON NEXT 100M;
-- 创建数据库用户
-- IDENTIFIED BY → 设置密码
-- DEFAULT TABLESPACE → 指定默认表空间
CREATE USER "root" IDENTIFIED BY "root"
DEFAULT TABLESPACE "rxthinkcmf_thinkphp6_elevue";
DEFAULT TABLESPACE "rxthinkcmf_laravel13_elevue";
-- 授权
-- CONNECT → 连接权限
@@ -249,22 +247,28 @@ EXIT;
# sqlplus → Oracle 命令行工具
# root/root@localhost:1521/XE → 用户/密码@主机:端口/服务名
# @file.sql → 执行 SQL 文件
sqlplus root/root@localhost:1521/XE @document/oracle/rxthinkcmf.thinkphp6.elevue.sql
sqlplus root/root@localhost:1521/XE @document/oracle/rxthinkcmf.laravel13.elevue.sql
```
### 3. 配置 .env
```ini
[DATABASE]
TYPE = oracle ; 数据库类型改为 oracle
HOSTNAME = 127.0.0.1
DATABASE = rxthinkcmf.thinkphp6.elevue
USERNAME = root
PASSWORD = root
HOSTPORT = 1521 ; Oracle 默认端口
PREFIX = think_
DB_CONNECTION=oracle
DB_HOST=127.0.0.1
DB_PORT=1521
DB_DATABASE=rxthinkcmf.laravel13.elevue
DB_USERNAME=root
DB_PASSWORD=root
DB_PREFIX=laravel_
```
:::warning Oracle 前置条件与实现说明
- 需要 PHP 的 `pdo_oci` 扩展与 Oracle Instant Client,安装步骤见 [2.1 开发环境准备](/zh/quickstart/environment.html)「数据库安装 → Oracle」;
- 项目已内置 Oracle 运行时驱动:`DB_CONNECTION=oracle` 即可连接(`AppServiceProvider` 注册 `oci` 自定义驱动,配套 `App\Support\Database\{OracleConnection, OracleGrammar, OracleProcessor}`,分页与自增主键回填已适配);切换时 `.env` 中 `DB_PORT` 改为 `1521`;
- 表结构变更请使用跨库迁移工具 `php artisan db:migrate`(自动生成 Oracle 序列与触发器),不使用 Laravel 迁移;
- 实际投产前请按业务场景完整验证(复杂查询、导入导出等)。
:::
---
## 验证数据库
@@ -272,8 +276,8 @@ PREFIX = think_
无论使用哪种数据库,都可以通过以下方式验证:
```bash
# 方式一:ThinkPHP 命令行(显示版本信息,验证框架可正常加载)
php think version
# 方式一:框架命令行(能正常执行说明框架与数据库配置可加载)
php artisan --version
# 方式二:手动测试登录接口(POST 请求 + JSON 参数)
curl http://localhost:8000/api/login -X POST \
@@ -288,33 +292,35 @@ curl http://localhost:8000/api/login -X POST \
# MySQL:查询用户表记录数(验证表是否创建成功且有数据)
# -----------------------------------------------------------------------------
# -e → 执行 SQL 后退出
mysql -u root -proot rxthinkcmf.thinkphp6.elevue -e "SELECT COUNT(*) FROM think_user;"
mysql -u root -proot rxthinkcmf.laravel13.elevue -e "SELECT COUNT(*) FROM laravel_user;"
# -----------------------------------------------------------------------------
# PostgreSQL:查询用户表记录数
# -----------------------------------------------------------------------------
# -c → 执行 SQL 命令
psql -U postgres -d rxthinkcmf.thinkphp6.elevue -c "SELECT COUNT(*) FROM think_user;"
psql -U postgres -d rxthinkcmf.laravel13.elevue -c "SELECT COUNT(*) FROM laravel_user;"
# -----------------------------------------------------------------------------
# SQLite:查询用户表记录数
# -----------------------------------------------------------------------------
sqlite3 /www/api/runtime/database.sqlite "SELECT COUNT(*) FROM think_user;"
sqlite3 database/database.sqlite "SELECT COUNT(*) FROM laravel_user;"
# -----------------------------------------------------------------------------
# SQL Server:查询用户表记录数
# -----------------------------------------------------------------------------
# -Q → 执行查询后退出
sqlcmd -S localhost -U sa -P root -d rxthinkcmf.thinkphp6.elevue -Q "SELECT COUNT(*) FROM think_user;"
sqlcmd -S localhost -U sa -P root -d rxthinkcmf.laravel13.elevue -Q "SELECT COUNT(*) FROM laravel_user;"
# -----------------------------------------------------------------------------
# Oracle:查询用户表记录数(需先进入 SQL 命令行)
# -----------------------------------------------------------------------------
sqlplus root/root@localhost:1521/XE
SELECT COUNT(*) FROM think_user;
SELECT COUNT(*) FROM laravel_user;
EXIT;
```
查询到记录数 > 0 即初始化成功。
---
## 默认数据
@@ -323,12 +329,16 @@ EXIT;
| 数据 | 说明 |
|------|------|
| 管理员账号 | admin / 123456 |
| 默认角色 | 超级管理员(data_scope=1,全部数据权限) |
| 管理员账号 | admin / 123456(超级管理员,用户 ID=1) |
| 租户演示账号 | tenant001 / 123456(客户租户A 租户管理员) |
| 默认角色 | 超级管理员(全部数据权限) |
| 部门与岗位 | 默认组织架构数据 |
| 系统菜单 | 完整的菜单和权限节点数据 |
| 字典数据 | 性别、状态等基础字典 |
| 系统参数 | DEFAULT_PASSWORD 等系统参数 |
| 系统配置 | 动态配置项 |
| 城市数据 | 全国行政区划数据 |
| 租户数据 | 平台租户与客户租户初始数据 |
---
@@ -338,35 +348,57 @@ EXIT;
```bash
# --default-character-set=utf8mb4 → 指定字符集为 utf8mb4(支持 emoji)
mysql -u root -proot --default-character-set=utf8mb4 rxthinkcmf.thinkphp6.elevue < document/mysql/rxthinkcmf.thinkphp6.elevue.sql
mysql -u root -proot --default-character-set=utf8mb4 rxthinkcmf.laravel13.elevue < document/mysql/rxthinkcmf.laravel13.elevue.sql
```
### Q: 表已存在错误
### Q: 报 `Table 'xxx.users' doesn't exist`
A: `DB_PREFIX` 未配置或不匹配。脚本中的表均为 `laravel_` 前缀(如 `laravel_user`),确认 `.env` 中:
```ini
DB_PREFIX=laravel_
```
修改后执行 `php artisan config:clear`。
### Q: 报错提示表已存在
```bash
# -----------------------------------------------------------------------------
# 先删除旧数据库再重建(⚠️ 会清空所有数据)
# -----------------------------------------------------------------------------
# DROP DATABASE IF EXISTS → 如果存在则删除
mysql -u root -proot -e "DROP DATABASE IF EXISTS \`rxthinkcmf.thinkphp6.elevue\`;"
mysql -u root -proot -e "DROP DATABASE IF EXISTS \`rxthinkcmf.laravel13.elevue\`;"
# 创建数据库(指定字符集)
mysql -u root -proot -e "CREATE DATABASE \`rxthinkcmf.thinkphp6.elevue\` DEFAULT CHARACTER SET utf8mb4;"
mysql -u root -proot -e "CREATE DATABASE \`rxthinkcmf.laravel13.elevue\` DEFAULT CHARACTER SET utf8mb4;"
# 重新导入 SQL
mysql -u root -proot rxthinkcmf.thinkphp6.elevue < document/mysql/rxthinkcmf.thinkphp6.elevue.sql
mysql -u root -proot rxthinkcmf.laravel13.elevue < document/mysql/rxthinkcmf.laravel13.elevue.sql
```
### Q: PostgreSQL 导入报错
### Q: 导入很慢或中断
确保使用 `document/postgresql/` 目录下的脚本,该脚本已针对 PostgreSQL 语法做了适配。
脚本约 226MB(含全国行政区划等大数据量表),导入需 1~3 分钟属正常。确保命令行窗口保持打开直到导入完成;中断后按上一条清库重导。
### Q: 使用了错误数据库类型的脚本
A: 请使用 `document/` 下**与你的数据库类型对应目录**中的脚本(如 MySQL 用 `document/mysql/`),脚本已按对应 SQL 语法适配,混用会报语法错误。
### Q: `php artisan migrate` 后依然表不存在
正常现象——本项目 migrations 为空,`migrate` 不创建业务表,请按本章手动导入 SQL 脚本。
### Q: SQLite 文件权限问题
```bash
# 664 → owner/group 可读写,others 只读
chmod 664 /www/api/runtime/database.sqlite
chmod 664 database/database.sqlite
# 修改文件所有者为 Web 服务器用户
chown www-data:www-data /www/api/runtime/database.sqlite
chown www-data:www-data database/database.sqlite
```
### Q: 想把已有 MySQL 数据迁移到其他数据库
使用跨库迁移工具(结构 + 数据 + Oracle 序列自动处理),见 [7.6 数据库迁移工具](/zh/tools/db-migrate.html)。
@@ -1,85 +1,110 @@
# 2.7 Docker 快速体验
# 2.8 Docker 快速体验
:::tip 概述
Docker 方案适合快速体验项目功能,无需手动安装 PHP、MySQL 等环境。本文提供完整的 Nginx + PHP-FPM 架构配置,复制即可使用。生产环境部署请参考[第 11 章](/zh/deploy/checklist.html)。
Docker 方案适合快速体验项目功能,无需在本机手动安装 PHP、MySQL、Redis 等环境。项目内置完整的 Nginx + PHP-FPM 架构配置,一条命令即可启动,脚本全文见下方(供参考,无需手动创建)。生产环境部署请参考 [11.6 Docker 生产部署](/zh/deploy/docker.html)。
:::
## 架构说明
```text
浏览器 → localhost:8000 → [Nginx 容器:80] → [PHP-FPM 容器:9000] → Laravel 应用
↕
[MySQL 容器:3306] + [Redis 容器:6379]
```
浏览器 → localhost:8000 → [Nginx 容器:80] → [PHP-FPM 容器:9000] → ThinkPHP
↕
[MySQL 容器:3306] + [Redis 容器:6379]
```
- **Nginx**:监听 80 端口(宿主机映射 8000),处理 HTTP 请求、URL 重写、静态资源缓存
- **PHP-FPM**:监听 9000 端口,执行 PHP 代码(镜像内置 PHP 8.3 + 扩展 + Composer 依赖)
- **MySQL**:数据库服务,数据持久化到 Docker 命名卷,**首次启动自动导入**数据库脚本
- **Redis**:缓存 / 会话 / 队列服务(JWT 会话、字典缓存均依赖),数据持久化到命名卷
## 文件结构
```
thinkphp6/
```text
RXThinkCMF_Laravel13_EleVue/
├── docker-compose.yml # 容器编排(项目根目录)
├── .dockerignore # 构建上下文排除(.env、vendor、SQL 等)
├── docker/
│ ├── Dockerfile # PHP-FPM 镜像定义
│ ├── nginx.conf # Nginx 站点配置
│ └── .env.docker # 环境变量(密码等)
├── .env # 应用配置
├── .env # 应用配置(Laravel 读取)
└── ...
```
## 前置要求
| 依赖 | 版本要求 | 说明 |
|------|----------|------|
| Docker Desktop | >= 20.10(含 Compose v2) | 已安装并**启动**,`docker compose version` 可验证 |
| 项目已初始化 | 根目录存在 `.env` 且 `APP_KEY` 已生成 | 未初始化见下方命令 |
| JWT_SECRET | `.env` 中 >= 32 字符 | 应用启动 fail-close 校验,不满足拒绝启动 |
尚未初始化时,先在宿主机执行(依赖 [2.1 开发环境准备](/zh/quickstart/environment.html) 中的 PHP 8.3 + Composer):
```bash
composer setup
# 等价于:composer install → 复制 .env → key:generate → migrate --force → npm install → npm run build
```
## 快速启动
项目已内置完整的 Docker 配置文件(`docker-compose.yml`、`docker/Dockerfile`、`docker/nginx.conf`),无需手动创建,直接启动即可:
```bash
# 从官方网站 (https://www.rxthink.cn) 下载授权源码包并解压
cd thinkphp6
# 配置应用环境变量
cp .env.example .env
# 编辑 .env,修改数据库连接地址为 Docker 服务名(见下方说明)
cd RXThinkCMF_Laravel13_EleVue
# 启动所有服务(首次会自动构建镜像,约 2-5 分钟)
docker-compose up -d
docker compose up -d
# 查看运行状态
docker-compose ps
# 查看运行状态(首次 mysql 需导入约 226MB SQL,初始化 1~3 分钟,
# 等到 mysql 变为 healthy,app 依赖健康检查会自动等待)
docker compose ps
# 查看日志(确认无报错)
docker-compose logs -f app
docker compose logs -f app
```
:::tip 说明
`docker-compose.yml` 使用 `${VAR:-默认值}` 语法,未配置 `.env.docker` 时自动使用默认密码(Your_Strong_Password_2024 / Your_Redis_Password_2024),适合快速体验。生产部署请参考[第 11 章](/zh/deploy/docker.html)使用 `--env-file` 指定生产密码。
`docker-compose.yml` 使用 `${VAR:-默认值}` 语法,未配置 `docker/.env.docker` 时自动使用默认密码(root123 / redis123),适合快速体验。生产部署请参考 [11.6 Docker 生产部署](/zh/deploy/docker.html) 使用 `--env-file` 指定生产密码。
:::
### 端口冲突处理(本机有 Laragon 时)
本机 Laragon 已占用 3306(MySQL)/ 6379(Redis)时,修改 `docker/.env.docker` 中的宿主机端口即可:
```ini
NGINX_PORT=8000 # Web 端口(被占用时改为 8080 等)
MYSQL_PORT=3307 # 与 Laragon MySQL 冲突时改为 3307
REDIS_PORT=6380 # 与 Laragon Redis 冲突时改为 6380
```
容器之间通过**服务名**(`mysql` / `redis`)互相通信,改宿主机端口不影响容器内部连接。
## docker-compose.yml
项目根目录的 `docker-compose.yml` 内容如下(供参考,无需手动创建):
```yaml
# =============================================================================
# Docker Compose — RXThinkCMF 容器编排
# Docker Compose — RXThinkCMF Laravel13 容器编排
# =============================================================================
# 架构:Nginx(80) → PHP-FPM(9000) → MySQL(3306) + Redis(6379)
#
# 快速启动(使用默认密码):
# docker-compose up -d
# docker compose up -d
#
# 生产部署(使用 .env.docker 中的密码):
# docker-compose --env-file docker/.env.docker up -d --build
# docker compose --env-file docker/.env.docker up -d --build
# =============================================================================
version: '3.8'
services:
# ===========================================================================
# Nginx 反向代理
# ===========================================================================
# 接收浏览器请求,静态资源直接返回,.php 请求转发给 PHP-FPM 容器
# ===========================================================================
nginx:
image: nginx:1.25-alpine
container_name: rxthinkcmf-nginx
container_name: rxthinkcmf-laravel13-nginx
ports:
- "${NGINX_PORT:-8000}:80" # 宿主机端口(默认 8000),可通过环境变量覆盖
volumes:
@@ -88,91 +113,113 @@ services:
depends_on:
- app
networks:
- rxthinkcmf-net
- rxthinkcmf-laravel13-net
restart: unless-stopped
# ===========================================================================
# PHP-FPM 应用服务
# ===========================================================================
# 执行 ThinkPHP 代码,处理 API 请求和业务逻辑
# 执行 Laravel 代码,处理 API 请求和业务逻辑
# ===========================================================================
app:
build:
context: . # 构建上下文为项目根目录
dockerfile: docker/Dockerfile # Dockerfile 路径
container_name: rxthinkcmf-app
container_name: rxthinkcmf-laravel13-app
volumes:
- ./:/var/www/html # 项目代码(可写)
- uploads_data:/opt/data/uploads # 上传文件持久化
- ./:/var/www/html # 项目代码(可写,上传文件随宿主机持久化)
environment:
- APP_DEBUG=${APP_DEBUG:-true} # 调试模式(快速体验默认开启)
# 数据库:容器内通过服务名 mysql 访问(覆盖 .env 中的 127.0.0.1)
- DB_CONNECTION=mysql
- DB_HOST=mysql
- DB_PORT=3306
- DB_DATABASE=${MYSQL_DATABASE:-rxthinkcmf.laravel13.elevue}
- DB_USERNAME=root
- DB_PASSWORD=${MYSQL_ROOT_PASSWORD:-root123}
# 缓存 / 会话 / 队列:统一走 Redis(数据库脚本不含 framework 表)
- CACHE_STORE=redis
- SESSION_DRIVER=redis
- QUEUE_CONNECTION=redis
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD:-redis123}
# JWT 签名密钥(应用启动要求 >= 32 字符)
- JWT_SECRET=${JWT_SECRET:-your_random_jwt_secret_key_at_least_32_bytes_long}
# 文件访问域名(接口返回上传文件的完整地址)
- FILE_DOMAIN_URL=${DOMAIN_URL:-http://localhost:8000}
# 跨域来源(快速体验默认放开)
- CORS_ALLOW_ORIGIN=${CORS_ALLOW_ORIGIN:-*}
depends_on:
mysql:
condition: service_healthy # 等待 MySQL 就绪
condition: service_healthy # 等待 MySQL 就绪(含首次 SQL 导入)
redis:
condition: service_started # 等待 Redis 启动
condition: service_started # 等待 Redis 启动
networks:
- rxthinkcmf-net
- rxthinkcmf-laravel13-net
restart: unless-stopped
# ===========================================================================
# MySQL 数据库
# ===========================================================================
# 存储业务数据,首次启动自动导入 init.sql
# 存储业务数据,首次启动自动导入数据库脚本
# ===========================================================================
mysql:
image: mysql:8.0
container_name: rxthinkcmf-mysql
container_name: rxthinkcmf-laravel13-mysql
ports:
- "${MYSQL_PORT:-3306}:3306" # 宿主机端口(默认 3306),可通过环境变量覆盖
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-Your_Strong_Password_2024} # root 密码
MYSQL_DATABASE: ${MYSQL_DATABASE:-rxthinkcmf.thinkphp6.elevue} # 自动创建的数据库
MYSQL_CHARSET: utf8mb4
MYSQL_COLLATION: utf8mb4_general_ci
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root123} # root 密码
MYSQL_DATABASE: ${MYSQL_DATABASE:-rxthinkcmf.laravel13.elevue} # 自动创建的数据库
volumes:
- mysql_data:/var/lib/mysql # 数据持久化
- ./document/mysql/rxthinkcmf.thinkphp6.elevue.sql:/docker-entrypoint-initdb.d/init.sql:ro # 初始化 SQL
- mysql_data:/var/lib/mysql # 数据持久化
- ./document/mysql/rxthinkcmf.laravel13.elevue.sql:/docker-entrypoint-initdb.d/init.sql:ro # 初始化 SQL(仅首次启动执行)
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_general_ci
- --default-authentication-plugin=mysql_native_password
healthcheck: # 健康检查:确保 MySQL 可接受连接
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD:-Your_Strong_Password_2024}"]
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD:-root123}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s # MySQL 启动较慢,给 30 秒缓冲
retries: 30
start_period: 60s # 首次启动需导入约 226MB SQL,给足缓冲时间
networks:
- rxthinkcmf-net
- rxthinkcmf-laravel13-net
restart: unless-stopped
# ===========================================================================
# Redis 缓存
# ===========================================================================
# 提供缓存加速,与 config/cache.php 中的 redis 驱动配合
# 提供缓存加速(JWT 会话/字典/权限缓存),与 config/database.php 中 redis 配合
# ===========================================================================
redis:
image: redis:7-alpine
container_name: rxthinkcmf-redis
container_name: rxthinkcmf-laravel13-redis
ports:
- "${REDIS_PORT:-6379}:6379" # 宿主机端口(默认 6379),可通过环境变量覆盖
command: redis-server --requirepass ${REDIS_PASSWORD:-Your_Redis_Password_2024} # 访问密码
command: redis-server --requirepass ${REDIS_PASSWORD:-redis123} # 访问密码
volumes:
- redis_data:/data # 数据持久化
networks:
- rxthinkcmf-net
- rxthinkcmf-laravel13-net
restart: unless-stopped
# =============================================================================
# 命名卷 — 持久化存储
# =============================================================================
# 容器删除后数据保留,彻底重置请执行:docker-compose down -v
# 容器删除后数据保留,彻底重置请执行:docker compose down -v
# =============================================================================
volumes:
mysql_data: # MySQL 数据文件
redis_data: # Redis RDB 快照
uploads_data: # 用户上传文件
# =============================================================================
# 内部网络
@@ -180,7 +227,7 @@ volumes:
# 容器间通过服务名(mysql / redis / app)互相访问
# =============================================================================
networks:
rxthinkcmf-net:
rxthinkcmf-laravel13-net:
driver: bridge
```
@@ -190,13 +237,14 @@ networks:
```dockerfile
# =============================================================================
# Dockerfile — PHP-FPM 应用镜像
# Dockerfile — PHP-FPM 应用镜像(RXThinkCMF Laravel13)
# =============================================================================
# 基础镜像:PHP 8.2 + FPM
# 构建命令:docker-compose build app
# 基础镜像:PHP 8.3 + FPM(FastCGI 进程管理器)
# 构建命令:docker compose build app
# 运行方式:由 Nginx 容器通过 FastCGI 协议调用本容器的 9000 端口
# =============================================================================
FROM php:8.2-fpm
FROM php:8.3-fpm
# -----------------------------------------------------------------------------
# 1. 安装系统依赖
@@ -204,18 +252,29 @@ FROM php:8.2-fpm
RUN apt-get update && apt-get install -y --no-install-recommends \
libpng-dev \ # GD 扩展 — PNG 支持
libjpeg-dev \ # GD 扩展 — JPEG 支持
libfreetype6-dev \ # GD 扩展 — 字体渲染(验证码)
libfreetype6-dev \ # GD 扩展 — 字体渲染(验证码依赖)
libzip-dev \ # zip 扩展依赖
zip \ # Composer 解压依赖
unzip \
&& rm -rf /var/lib/apt/lists/*
zip \ # Composer 安装依赖包时可能用到
unzip \ # Composer 解压依赖包
&& rm -rf /var/lib/apt/lists/* # 清理 apt 缓存,减小镜像体积
# -----------------------------------------------------------------------------
# 2. 安装 PHP 扩展
# -----------------------------------------------------------------------------
# pdo_mysql=MySQL驱动 mbstring=多字节 gd=图片 bcmath=高精度 zip=压缩 redis=缓存
# 项目必需扩展(根据 composer.json 和 config/ 推导):
# pdo_mysql — MySQL 数据库驱动(config/database.php 使用)
# mbstring — 多字节字符串处理(Laravel 框架依赖)
# gd — 图片处理(验证码生成、图片缩放)
# bcmath — 高精度数学运算(phpspreadsheet 依赖)
# zip — ZIP 压缩(phpspreadsheet 读写 xlsx 依赖)
# redis — Redis 扩展(phpredis:缓存/会话/队列/JWT 会话均依赖)
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) pdo_mysql mbstring gd bcmath zip \
&& docker-php-ext-install -j$(nproc) \
pdo_mysql \
mbstring \
gd \
bcmath \
zip \
&& pecl install redis \
&& docker-php-ext-enable redis
@@ -225,36 +284,62 @@ RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# -----------------------------------------------------------------------------
# 4. PHP 生产配置
# 4. 配置 PHP 生产环境参数
# -----------------------------------------------------------------------------
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
&& echo '\
date.timezone = Asia/Shanghai\n\
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
RUN echo '\n\
; ====== 生产环境追加配置(Docker 自动生成) ====== \n\
\n\
display_errors = Off\n\
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED\n\
memory_limit = 256M\n\
max_execution_time = 60\n\
max_input_time = 60\n\
upload_max_filesize = 50M\n\
post_max_size = 50M\n\
max_execution_time = 60\n\
max_file_uploads = 20\n\
date.timezone = Asia/Shanghai\n\
opcache.enable = 1\n\
opcache.memory_consumption = 256\n\
opcache.interned_strings_buffer = 16\n\
opcache.max_accelerated_files = 10000\n\
opcache.validate_timestamps = 1\n\
opcache.revalidate_freq = 0\n\
opcache.save_comments = 1\n\
' >> "$PHP_INI_DIR/php.ini"
# -----------------------------------------------------------------------------
# 5. 安装项目依赖
# 5. 设置工作目录
# -----------------------------------------------------------------------------
WORKDIR /var/www/html
# -----------------------------------------------------------------------------
# 6. 复制项目文件并安装依赖(利用 Docker 层缓存)
# -----------------------------------------------------------------------------
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-interaction
COPY . .
# -----------------------------------------------------------------------------
# 6. 目录权限
# 7. 设置 Laravel 可写目录权限
# -----------------------------------------------------------------------------
RUN mkdir -p runtime uploads \
&& chown -R www-data:www-data runtime uploads \
&& chmod -R 775 runtime uploads
# storage — 日志、缓存、会话、编译视图(必须可写)
# bootstrap/cache — 配置/路由/事件缓存(必须可写)
# public/uploads — 用户上传文件目录(必须可写)
RUN mkdir -p \
storage/framework/cache \
storage/framework/sessions \
storage/framework/views \
storage/logs \
bootstrap/cache \
public/uploads \
&& chown -R www-data:www-data storage bootstrap/cache public/uploads \
&& chmod -R 775 storage bootstrap/cache public/uploads
# -----------------------------------------------------------------------------
# 8. 暴露端口并启动
# -----------------------------------------------------------------------------
EXPOSE 9000
CMD ["php-fpm"]
```
@@ -265,53 +350,64 @@ CMD ["php-fpm"]
```nginx
# =============================================================================
# Nginx 配置 — ThinkPHP 6 反向代理
# Nginx 配置 — RXThinkCMF Laravel13 反向代理
# =============================================================================
server {
# 监听容器内 80 端口(宿主机通过 docker-compose ports 映射到 8000)
listen 80;
server_name localhost;
# ThinkPHP 入口目录
# 网站根目录指向 Laravel 公开目录(容器内代码挂载在 /var/www/html)
root /var/www/html/public;
index index.php;
# URL 重写(ThinkPHP 路由必需)
# -----------------------------------------------------------------
# URL 重写(Laravel 路由)
# -----------------------------------------------------------------
# 真实文件直接返回,其余交给 public/index.php 由 Laravel 路由解析
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=$1 last;
}
try_files $uri $uri/ /index.php?$query_string;
}
# PHP-FPM 转发("app" 是 docker-compose 中的服务名)
location ~ \.php$ {
fastcgi_pass app:9000;
# -----------------------------------------------------------------
# PHP-FPM 处理(仅入口 index.php 可执行)
# -----------------------------------------------------------------
# "app" 是 docker-compose 中 PHP-FPM 服务的名称,Docker 内部 DNS 自动解析
location = /index.php {
fastcgi_pass app:9000; # 转发到 PHP-FPM 容器的 9000 端口
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 300;
include fastcgi_params; # 引入标准 FastCGI 参数
fastcgi_read_timeout 300; # 超时设置(秒),防止长请求被提前断开
fastcgi_send_timeout 300;
}
# 安全:禁止访问隐藏文件
# 其余 .php 文件(含上传目录)一律 404,防止恶意脚本被执行
location ~ \.php$ {
return 404;
}
# -----------------------------------------------------------------
# 安全规则:禁止访问隐藏文件(如 .env、.git、.htaccess)
# -----------------------------------------------------------------
location ~ /\. {
deny all;
}
# 安全:禁止上传目录执行 PHP
location ~ /uploads/.*\.php$ {
deny all;
}
# -----------------------------------------------------------------
# 静态资源缓存 30 天
# -----------------------------------------------------------------
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# -----------------------------------------------------------------
# Gzip 压缩
# -----------------------------------------------------------------
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
gzip_min_length 1024;
gzip_comp_level 5;
}
@@ -323,28 +419,39 @@ server {
```ini
# Docker 环境变量(密码等敏感信息,切勿提交 Git)
# 端口(宿主机端口,冲突时修改)
NGINX_PORT=8000
MYSQL_PORT=3306
REDIS_PORT=6379
# MySQL(首次启动自动导入 document/mysql/rxthinkcmf.laravel13.elevue.sql)
MYSQL_ROOT_PASSWORD=Your_Strong_Password_2024
MYSQL_DATABASE=rxthinkcmf.thinkphp6.elevue
MYSQL_DATABASE=rxthinkcmf.laravel13.elevue
# Redis
REDIS_PASSWORD=Your_Redis_Password_2024
# 应用
JWT_SECRET=<32 字符以上随机值,openssl rand -base64 32 生成>
APP_DEBUG=false
DOMAIN_URL=http://localhost:8000
CORS_ALLOW_ORIGIN=http://localhost:8000
```
修改项目根目录 `.env` 文件中的数据库和缓存连接:
:::tip 与本地 .env 的关系
`docker-compose.yml` 的 `environment` 块会把数据库 / Redis / JWT 等配置**以容器环境变量注入,优先级高于 `.env` 文件**——因此容器内自动使用服务名 `mysql` / `redis`,**无需修改本地 `.env`**(与手动部署用同一份 `.env` 互不干扰)。
:::
```ini
[DATABASE]
; 连接地址(Docker 内部使用服务名 "mysql")
HOSTNAME = mysql
PASSWORD = Your_Strong_Password_2024
## 配置优先级
[CACHE]
; 连接地址(Docker 内部使用服务名 "redis")
REDIS_HOST = redis
REDIS_PASSWORD = Your_Redis_Password_2024
变量插值优先级(高 → 低):
[FILE]
; 上传目录(容器内路径)
UPLOAD_DIR = /opt/data/uploads
```text
1. shell 环境变量
2. --env-file 指定的文件(如 docker/.env.docker)
3. 项目根目录 .env(compose 自动读取)
4. docker-compose.yml 中的 ${VAR:-默认值}
```
## 访问服务
@@ -353,22 +460,37 @@ UPLOAD_DIR = /opt/data/uploads
| 服务 | 地址 | 说明 |
|------|------|------|
| 后端 API | http://localhost:8000 | Nginx → PHP-FPM → ThinkPHP |
| MySQL | localhost:3306 | 用户名 root,密码 Your_Strong_Password_2024 |
| Redis | localhost:6379 | 密码 Your_Redis_Password_2024 |
| 后端 API | http://localhost:8000 | Nginx → PHP-FPM → Laravel |
| 健康检查 | http://localhost:8000/up | Laravel 内置健康路由 |
| MySQL | localhost:3306 | 用户名 root,默认密码 root123 |
| Redis | localhost:6379 | 默认密码 redis123 |
验证后端是否正常:
```bash
# 访问首页
curl http://localhost:8000/
# 健康检查
curl http://localhost:8000/up
# 登录获取 Token
# 验证码(同时验证 PHP→Redis 链路,返回 code=0 即成功)
curl http://localhost:8000/api/captcha
# → {"code":0,"ok":true,"msg":"操作成功","data":{...}}
# 登录获取 Token(默认管理员账号 admin / 123456,登录后立即修改)
# 验证码参数 key/code 与 /api/captcha 响应字段一致
curl -X POST http://localhost:8000/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"123456","captcha":"618618"}'
-d '{"username":"admin","password":"123456","key":"<captcha_key>","code":"<验证码字符>"}'
```
## 数据持久化
| 数据 | 位置 | `down` 后 | `down -v` 后 |
|------|------|-----------|--------------|
| MySQL 数据 | 命名卷 `mysql_data` | 保留 | **删除** |
| Redis 数据 | 命名卷 `redis_data` | 保留 | **删除** |
| 上传文件 | 宿主机项目目录(挂载) | 保留 | 保留 |
| 代码 | 宿主机项目目录(挂载) | 保留 | 保留 |
## 常用命令
```bash
@@ -376,52 +498,56 @@ curl -X POST http://localhost:8000/api/login \
# 服务管理
# --------------------------------------------------------------------------
# 启动服务
docker-compose up -d
docker compose up -d
# 停止服务(保留数据)
docker-compose down
docker compose down
# 停止并清空所有数据(重新开始)
docker-compose down -v
docker compose down -v
# 重建镜像(Dockerfile 或 composer.json 变更后)
docker-compose up -d --build
docker compose up -d --build
# --------------------------------------------------------------------------
# 日志排查
# --------------------------------------------------------------------------
# 查看所有日志
docker-compose logs -f
docker compose logs -f
# 查看应用日志
docker-compose logs -f app
# 查看应用日志 / 数据库日志(首次看导入进度)
docker compose logs -f app
docker compose logs -f mysql
# --------------------------------------------------------------------------
# 进入容器
# --------------------------------------------------------------------------
# 进入 PHP-FPM 容器
docker-compose exec app bash
docker compose exec app bash
# 进入 MySQL 容器
docker-compose exec mysql bash
docker compose exec mysql bash
# --------------------------------------------------------------------------
# 数据库操作
# --------------------------------------------------------------------------
# 导入 SQL(手动方式)
docker-compose exec -T mysql mysql -uroot -pYour_Strong_Password_2024 rxthinkcmf.thinkphp6.elevue < document/mysql/rxthinkcmf.thinkphp6.elevue.sql
# 导入 SQL(手动方式,首次启动已自动导入)
docker compose exec -T mysql mysql -uroot -pYour_Strong_Password_2024 rxthinkcmf.laravel13.elevue < document/mysql/rxthinkcmf.laravel13.elevue.sql
# 进入 MySQL 命令行
docker-compose exec mysql mysql -uroot -pYour_Strong_Password_2024 rxthinkcmf.thinkphp6.elevue
docker compose exec mysql mysql -uroot -pYour_Strong_Password_2024 rxthinkcmf.laravel13.elevue
# --------------------------------------------------------------------------
# ThinkPHP 命令
# Laravel Artisan 命令
# --------------------------------------------------------------------------
# 清除缓存
docker-compose exec app php think clear
# 清除配置缓存
docker compose exec app php artisan config:clear
# 查看路由
docker-compose exec app php think route:list
# 容器内执行 tinker 验证 Redis 连通
docker compose exec app php artisan tinker
# 查看路由列表
docker compose exec app php artisan route:list
```
## 常见问题
@@ -435,37 +561,62 @@ netstat -ano | findstr :8000
# Linux/Mac
lsof -i :8000
# 解决方案:修改 docker-compose.yml 中的端口映射
# ports:
# - "8080:80" # 改为 8080
# 解决方案:修改 docker/.env.docker 中的端口
# NGINX_PORT=8080 # 改为 8080
```
### 数据库连接失败
确认 `.env` 中 `[DATABASE]` 下 `HOSTNAME = mysql`(Docker 服务名),不是 `127.0.0.1`。
`docker-compose.yml` 的 `environment` 块会把 `DB_HOST` 覆盖为 `mysql`(Docker 服务名)。若手动改过 compose 导致覆盖失效,确认容器内环境变量 `DB_HOST=mysql`,不是 `127.0.0.1`:
```bash
docker compose exec app printenv DB_HOST
# → mysql
```
### 容器启动后立即退出
```bash
# 查看退出原因
docker-compose logs app
docker compose logs app
# 常见原因:
# 1. PHP 扩展缺失 → 检查 Dockerfile 中的 docker-php-ext-install
# 2. 目录权限问题 → 确认 runtime 和 uploads 目录可写
# 3. composer install 失败 → 检查网络或镜像源
# 1. JWT_SECRET 长度不足(< 32 字符)→ 应用 fail-close 拒绝启动,重新生成密钥
# 2. PHP 扩展缺失 → 检查 Dockerfile 中的 docker-php-ext-install
# 3. 目录权限问题 → 确认 storage 和 bootstrap/cache 目录可写
# 4. composer install 失败 → 检查网络或镜像源
```
### 首次启动 mysql 一直 unhealthy
首次启动需导入约 226MB 数据库脚本,初始化 1~3 分钟属正常。查看进度:
```bash
docker compose logs -f mysql
# 等待 "ready for connections" 出现后,mysql 变为 healthy,app 自动启动
```
### 验证码一直失败
Redis 未启动或密码不匹配:
```bash
docker compose ps # 确认 redis 容器状态
docker compose logs redis # 查看 Redis 日志
docker compose exec app php artisan tinker # 容器内验证连通
```
### 重新开始
```bash
# 删除所有容器、网络、数据卷,完全重来
docker-compose down -v
docker-compose up -d --build
docker compose down -v
docker compose up -d --build
```
## 相关文档
- [生产部署](/zh/deploy/docker.html) — 生产环境完整方案
- [生产部署](/zh/deploy/docker.html) — 生产环境完整方案与安全加固
- [后端启动](/zh/quickstart/backend-start.html) — 不用 Docker,手动启动
- [数据库初始化](/zh/quickstart/database-init.html) — 手动导入 SQL
- [环境变量配置](/zh/quickstart/env-vars.html) — `.env` 全部配置项说明
@@ -1,225 +1,262 @@
# 2.5 环境变量配置
# 2.6 环境变量配置
:::tip 概述
项目使用 `.env` 文件管理环境变量,支持开发、测试、生产等多环境切换。配置优先级:`.env` > `config/*.php` > 代码默认值。
项目使用 `.env` 文件管理环境变量,支持开发、测试、生产等多环境切换。配置优先级:**真实环境变量 > `.env` > `config/*.php` 中的默认值**。模板文件为 `.env.example`,首次部署复制为 `.env` 后修改。
:::
## .env 文件结构
项目 `.env` 文件采用 INI 分组格式,每个分组对应一个 `config/*.php` 配置文件:
项目 `.env` 采用 Laravel 标准的 `KEY=VALUE` 扁平格式(无分组),每个键按前缀对应一个 `config/*.php` 配置文件,通过 `env()` 函数读取:
```ini
; =============================================================================
; .env 环境变量配置文件
; =============================================================================
; 格式:INI 分组格式,[分组] 下的键值对通过 env() 函数读取
; 优先级:.env > config/*.php > 代码默认值
; =============================================================================
# =============================================================================
# .env 环境变量配置文件(Laravel 标准格式)
# =============================================================================
# 格式:KEY=VALUE 扁平结构,按前缀映射到 config/*.php(如 JWT_* → config/jwt.php)
# 优先级:真实环境变量 > .env > config/*.php 默认值
# 修改后执行 php artisan config:clear 清除配置缓存
# =============================================================================
; 全局开关(不属于任何分组)
APP_DEBUG = true ; 调试模式(开发 true,生产 false)
# ─── 应用基础 ─────────────────────────────────────────────
APP_NAME=Laravel # 应用名称
APP_ENV=local # 环境标识(local / production)
APP_KEY= # 应用加密密钥(php artisan key:generate 生成)
APP_DEBUG=true # 调试模式(开发 true,生产必须 false)
APP_URL=http://localhost # 应用访问地址
APP_TIMEZONE=Asia/Shanghai # 应用时区(写入时间字段用,需与 MySQL、PHP 一致)
APP_LOCALE=en # 默认语言
BCRYPT_ROUNDS=12 # bcrypt 成本因子(与 PasswordService 一致)
[APP]
DEFAULT_TIMEZONE = Asia/Shanghai ; 默认时区
DEMO = false ; 演示模式(true=拦截所有写操作)
SHOW_ERROR_MSG = true ; 显示错误详情(生产环境设为 false)
# ─── 日志 ─────────────────────────────────────────────────
LOG_CHANNEL=stack # 日志通道
LOG_LEVEL=debug # 日志级别(生产改为 info)
[DATABASE]
TYPE = mysql ; 数据库类型(mysql / pgsql / sqlsrv / oracle / sqlite)
HOSTNAME = 127.0.0.1 ; 数据库服务器地址
DATABASE = rxthinkcmf.thinkphp6.elevue ; 数据库名
USERNAME = root ; 用户名
PASSWORD = root ; 密码
HOSTPORT = 3306 ; 端口
CHARSET = utf8mb4 ; 字符集(支持 emoji)
DEBUG = true ; SQL 调试(显示执行的 SQL 语句)
PREFIX = think_ ; 表前缀
# ─── 数据库 ───────────────────────────────────────────────
DB_CONNECTION=mysql # 数据库类型(mysql / pgsql / sqlsrv / sqlite)
DB_HOST=127.0.0.1 # 数据库地址
DB_PORT=3306 # 端口
DB_DATABASE=rxthinkcmf.laravel13.elevue # 数据库名
DB_USERNAME=root # 用户名
DB_PASSWORD=root # 密码
DB_PREFIX=laravel_ # 表前缀(脚本使用 laravel_ 前缀,必须保留)
[JWT]
SECRET = rxthinkcmf_jwt_secret_key_2024 ; 签名密钥(生产环境务必修改)
ACCESS_TTL = 7200 ; access_token 有效期(秒),默认 2 小时
REFRESH_TTL = 604800 ; refresh_token 有效期(秒),默认 7 天
ISSUER = rxthinkcmf ; 签发者标识
# ─── 会话 / 缓存 / 队列(统一走 Redis,必需)──────────────
SESSION_DRIVER=redis # 会话驱动
SESSION_LIFETIME=120 # 会话有效期(分钟)
QUEUE_CONNECTION=redis # 队列驱动
CACHE_STORE=redis # 缓存驱动
[FILE]
UPLOAD_DIR = D:/uploads/rxthinkcmf ; 上传根目录(绝对路径)
DOMAIN_URL = http://file.thinkphp6.elevue ; 文件访问域名
IMAGE_EXT = gif,jpg,jpeg,png,bmp,webp,svg ; 允许的图片后缀
FILE_EXT = gif,jpg,jpeg,png,bmp,webp,svg,doc,docx,xls,xlsx,ppt,pptx,pdf,zip,rar,txt,csv,mp3,mp4,sql,js,css ; 允许的文件后缀
MAX_SIZE = 10 ; 单文件最大大小(MB)
# ─── Redis ────────────────────────────────────────────────
REDIS_CLIENT=phpredis # 客户端(phpredis 扩展)
REDIS_HOST=127.0.0.1 # Redis 地址
REDIS_PASSWORD=null # Redis 密码(设置了 requirepass 时必须同步)
REDIS_PORT=6379 # Redis 端口
[CACHE]
DRIVER = file ; 缓存驱动(file=文件,redis=Redis)
REDIS_HOST = 127.0.0.1 ; Redis 地址(driver=redis 时生效)
REDIS_PORT = 6379 ; Redis 端口
REDIS_PASSWORD = ; Redis 密码(无密码留空)
REDIS_DB = 0 ; Redis 数据库编号
REDIS_PREFIX = rx: ; 缓存键前缀
# ─── JWT ──────────────────────────────────────────────────
JWT_SECRET= # 签名密钥(必须 >= 32 字符,启动 fail-close 校验)
JWT_ACCESS_TTL=7200 # access_token 有效期(秒),默认 2 小时
JWT_REFRESH_TTL=604800 # refresh_token 有效期(秒),默认 7 天
JWT_PERMS_TTL=7200 # 权限缓存 TTL(秒),建议不小于 refresh_ttl
JWT_ISSUER=rxthinkcmf # 签发者标识
[CORS]
ALLOW_ORIGIN = * ; 允许的来源(* = 所有)
# ─── 文件上传 ─────────────────────────────────────────────
FILE_UPLOAD_DIR=uploads # 上传根目录(相对 public/)
FILE_DOMAIN_URL= # 文件访问域名(空则返回相对路径)
FILE_MAX_SIZE=10 # 单文件大小上限(MB)
FILE_IMAGE_EXT=gif,jpg,jpeg,png,bmp,webp # 允许的图片后缀(svg 已移除:XSS 风险)
FILE_EXT=gif,jpg,jpeg,png,bmp,webp,doc,docx,xls,xlsx,ppt,pptx,pdf,zip,rar,txt,csv # 允许的文件后缀
[API]
CAMEL_SNAKE_CONVERT = true ; 驼峰/下划线自动转换
# ─── CORS 跨域 ────────────────────────────────────────────
CORS_ALLOW_ORIGIN=* # 允许来源(逗号分隔;开发 *,生产改为具体域名)
[CAPTCHA]
CAPTCHA_BYPASS_CODE = ; 万能验证码(开发用,生产务必留空)
CAPTCHA_LENGTH = 6 ; 验证码字符长度(4-6位)
CAPTCHA_EXPIRE = 300 ; 验证码过期时间(秒)
# ─── API ──────────────────────────────────────────────────
API_CAMEL_SNAKE_CONVERT=true # 请求/响应键名驼峰与下划线自动转换
[LANG]
default_lang = zh-cn ; 默认语言
# ─── 多租户 ───────────────────────────────────────────────
DEFAULT_TENANT_ID=1 # 平台用户新增数据时的默认租户 ID
# ─── 演示模式 ─────────────────────────────────────────────
DEMO_MODE=false # true=拦截写操作(POST/PUT/DELETE/PATCH),#[DemoAllow] 例外
# ─── 受信代理 ─────────────────────────────────────────────
TRUSTED_PROXIES= # 逗号分隔的代理 IP 或 CIDR(CDN/负载均衡场景)
# ─── 验证码 ───────────────────────────────────────────────
CAPTCHA_LENGTH=6 # 字符长度
CAPTCHA_CHARS=ABCDEFGHJKLMNPQRSTUVWXYZ2346789 # 字符集(已排除 0O、1lI 易混淆字符)
CAPTCHA_EXPIRE=300 # 有效期(秒)
CAPTCHA_RATE_WINDOW=60 # 防刷窗口期(秒)
CAPTCHA_RATE_LIMIT=10 # 窗口期内最大获取次数
CAPTCHA_BYPASS_CODE= # 万能验证码(仅 APP_DEBUG=true 生效,生产务必留空)
# ─── 代码生成器 ───────────────────────────────────────────
GENERATOR_PARENT_MENU_ID=189 # 生成模块的父菜单 ID
GENERATOR_APP_NAME=tool # 应用名(路由前缀、前端目录)
```
:::danger 重要键说明
1. `JWT_SECRET` 为空或不足 32 字符时,应用**拒绝启动**(fail-close)。生成:`openssl rand -base64 32`。
2. `DB_PREFIX=laravel_` 必须保留,漏配会报表不存在。
3. `REDIS_PASSWORD` 与 Redis 实际密码必须一致,否则验证码、登录全部失败。
4. `APP_DEBUG` 生产环境必须为 `false`(否则异常堆栈泄露给前端)。
:::
## 配置文件映射
| .env 键前缀 | 配置文件 | 说明 |
|-------------|----------|------|
| `APP_*` | `config/app.php` | 应用名称 / 环境 / 调试 / 密钥 |
| `DB_*` | `config/database.php` | 数据库连接 + Redis 连接 |
| `SESSION_*` | `config/session.php` | 会话 |
| `CACHE_*` / `QUEUE_*` | `config/cache.php` / `config/queue.php` | 缓存 / 队列 |
| `JWT_*` | `config/jwt.php` ★ | JWT 密钥与有效期 |
| `FILE_*` | `config/file.php` ★ | 文件上传 |
| `CORS_ALLOW_ORIGIN` | `config/cors.php` ★ | 跨域 |
| `API_*` | `config/api.php` ★ | 驼峰转换开关 |
| `CAPTCHA_*` | `config/captcha.php` ★ | 验证码 |
| `DEFAULT_TENANT_ID` | `config/tenant.php` ★ | 多租户 |
| `GENERATOR_*` | `config/generator.php` | 代码生成器 |
| `TRUSTED_PROXIES` | `config/middleware.php` ★ | 受信代理 |
| `LOG_*` | `config/logging.php` | 日志 |
| `MAIL_*` | `config/mail.php` | 邮件 |
标 ★ 为本项目独立维护的配置文件(见 [1.3 目录结构](/zh/directory.html))。
## 配置项详解
### 应用配置 `[APP]`
### 应用配置
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `DEFAULT_TIMEZONE` | `Asia/Shanghai` | 默认时区 |
| `DEMO` | `false` | 演示模式,开启后拦截所有写操作 |
| `SHOW_ERROR_MSG` | `true` | 是否显示错误详情,生产环境设为 `false` |
| `APP_NAME` | Laravel | 应用名称 |
| `APP_ENV` | local | 环境标识 |
| `APP_KEY` | (空) | 应用加密密钥,`key:generate` 生成 |
| `APP_DEBUG` | true | 调试模式,**生产必须 false** |
| `APP_URL` | http://localhost | 应用访问地址 |
| `APP_TIMEZONE` | Asia/Shanghai | 应用时区,`create_time` / `update_time` 等时间字段按此写入,需与 MySQL、PHP 时区一致 |
| `BCRYPT_ROUNDS` | 12 | 密码哈希成本因子 |
| `DEMO_MODE` | false | 演示模式,开启后拦截所有写操作 |
| `DEFAULT_TENANT_ID` | 1 | 平台用户新增数据的默认租户 ID |
| `TRUSTED_PROXIES` | (空) | 受信代理 IP/CIDR,CDN 后取真实 IP 用 |
全局变量(不属于任何分组):
### 数据库配置
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `APP_DEBUG` | `true` | 调试模式,生产环境设为 `false` |
| `DB_CONNECTION` | sqlite | 数据库类型:mysql / pgsql / sqlsrv / sqlite |
| `DB_HOST` | 127.0.0.1 | 数据库主机 |
| `DB_PORT` | 3306 | 数据库端口 |
| `DB_DATABASE` | - | 数据库名(SQLite 为文件路径) |
| `DB_USERNAME` | root | 用户名 |
| `DB_PASSWORD` | - | 密码 |
| `DB_PREFIX` | laravel_ | 表前缀 |
### 数据库配置 `[DATABASE]`
### JWT 配置
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `TYPE` | `mysql` | 数据库类型:mysql / pgsql / sqlsrv / oracle / sqlite |
| `HOSTNAME` | `127.0.0.1` | 数据库主机 |
| `DATABASE` | - | 数据库名称(SQLite 为文件路径) |
| `USERNAME` | `root` | 数据库用户名 |
| `PASSWORD` | - | 数据库密码 |
| `HOSTPORT` | `3306` | 数据库端口 |
| `CHARSET` | `utf8mb4` | 字符集 |
| `DEBUG` | `true` | 是否开启 SQL 调试 |
| `PREFIX` | `think_` | 表前缀 |
| `JWT_SECRET` | (必填) | 签名密钥,**>= 32 字符,否则拒绝启动** |
| `JWT_ACCESS_TTL` | 7200 | access_token 有效期(秒),默认 2 小时 |
| `JWT_REFRESH_TTL` | 604800 | refresh_token 有效期(秒),默认 7 天 |
| `JWT_PERMS_TTL` | 7200 | 权限缓存 TTL(秒) |
| `JWT_ISSUER` | rxthinkcmf | 签发者标识 |
### JWT 配置 `[JWT]`
### Redis / 缓存配置
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `SECRET` | 内置默认值 | 签名密钥,**生产环境务必修改** |
| `ACCESS_TTL` | `7200` | access_token 有效期(秒),默认 2 小时 |
| `REFRESH_TTL` | `604800` | refresh_token 有效期(秒),默认 7 天 |
| `ISSUER` | `rxthinkcmf` | 签发者标识 |
| `REDIS_CLIENT` | phpredis | 客户端(需 PHP redis 扩展) |
| `REDIS_HOST` | 127.0.0.1 | Redis 主机 |
| `REDIS_PASSWORD` | null | Redis 密码(无密码保持 null) |
| `REDIS_PORT` | 6379 | Redis 端口 |
| `SESSION_DRIVER` | redis | 会话驱动 |
| `CACHE_STORE` | redis | 缓存驱动 |
| `QUEUE_CONNECTION` | redis | 队列驱动 |
### 文件上传配置 `[FILE]`
:::warning
会话 / 缓存 / 队列驱动**不要改回 `database`**——数据库脚本不含 Laravel framework 表(`sessions` / `cache` / `jobs`),改回后登录与缓存会直接报表不存在。
:::
### 文件上传配置
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `UPLOAD_DIR` | - | 上传根目录(绝对路径) |
| `DOMAIN_URL` | - | 文件访问域名 |
| `IMAGE_EXT` | `gif,jpg,jpeg,png,bmp,webp,svg` | 允许的图片后缀 |
| `FILE_UPLOAD_DIR` | uploads | 上传根目录(相对 `public/`) |
| `FILE_DOMAIN_URL` | (空) | 文件访问域名前缀,空则返回相对路径 |
| `FILE_MAX_SIZE` | 10 | 单文件大小上限(MB) |
| `FILE_IMAGE_EXT` | gif,jpg,... | 允许的图片后缀(**svg 已移除**:内嵌 JS 导致 XSS 风险) |
| `FILE_EXT` | 多种后缀 | 允许的文件后缀 |
| `MAX_SIZE` | `10` | 单文件最大大小(MB) |
### 缓存配置 `[CACHE]`
### CORS / API 配置
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `DRIVER` | `file` | 缓存驱动:`file` 或 `redis` |
| `REDIS_HOST` | `127.0.0.1` | Redis 主机(driver=redis 时生效) |
| `REDIS_PORT` | `6379` | Redis 端口(driver=redis 时生效) |
| `REDIS_PASSWORD` | - | Redis 密码 |
| `REDIS_DB` | `0` | Redis 数据库编号 |
| `REDIS_PREFIX` | `rx:` | 缓存键前缀 |
| `CORS_ALLOW_ORIGIN` | * | 允许来源,逗号分隔;凭证模式下会回显具体 Origin |
| `API_CAMEL_SNAKE_CONVERT` | true | 请求/响应键名驼峰与下划线自动转换 |
### CORS 配置 `[CORS]`
### 验证码配置
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `ALLOW_ORIGIN` | `*` | 允许的来源,`*` 表示所有 |
| `CAPTCHA_BYPASS_CODE` | (空) | 万能验证码,**仅 APP_DEBUG=true 时生效,生产务必留空** |
| `CAPTCHA_LENGTH` | 6 | 验证码字符长度(4-6 位) |
| `CAPTCHA_EXPIRE` | 300 | 验证码过期时间(秒),默认 5 分钟 |
| `CAPTCHA_RATE_WINDOW` | 60 | 防刷窗口期(秒) |
| `CAPTCHA_RATE_LIMIT` | 10 | 窗口期内最大获取次数 |
| `CAPTCHA_CHARS` | ABCDEFGHJKLMNPQRSTUVWXYZ2346789 | 字符集(已排除 0O、1lI 等易混淆字符) |
| `CAPTCHA_FONT_PATH` | static/fonts/Vera.ttf | 字体路径 |
| `CAPTCHA_IMAGE_WIDTH` | 160 | 图片宽度(像素) |
| `CAPTCHA_IMAGE_HEIGHT` | 50 | 图片高度(像素) |
| `CAPTCHA_FONT_SIZE` | 28 | 字体大小 |
| `CAPTCHA_NOISE_LINES` | 6 | 干扰线数量 |
| `CAPTCHA_NOISE_DOTS` | 300 | 噪点数量 |
### API 配置 `[API]`
### 代码生成器配置
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `CAMEL_SNAKE_CONVERT` | `true` | 驼峰/下划线自动转换开关 |
### 验证码配置 `[CAPTCHA]`
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `CAPTCHA_BYPASS_CODE` | (空) | 万能验证码,跳过验证。仅用于开发调试,**生产环境务必留空** |
| `CAPTCHA_LENGTH` | `6` | 验证码字符长度(4-6位) |
| `CAPTCHA_EXPIRE` | `300` | 验证码过期时间(秒),默认 5 分钟 |
| `CAPTCHA_RATE_WINDOW` | `60` | 防刷窗口期(秒) |
| `CAPTCHA_RATE_LIMIT` | `20` | 窗口期内最大获取次数 |
| `CAPTCHA_CHARS` | `ABCDEFGHJKLMNPQRSTUVWXYZ2346789` | 字符集(已排除 0O、1lI 等易混淆字符) |
| `CAPTCHA_FONT_PATH` | `static/fonts/Vera.ttf` | 字体路径 |
| `CAPTCHA_IMAGE_WIDTH` | `160` | 图片宽度(像素) |
| `CAPTCHA_IMAGE_HEIGHT` | `50` | 图片高度(像素) |
| `CAPTCHA_FONT_SIZE` | `28` | 字体大小 |
| `CAPTCHA_NOISE_LINES` | `8` | 干扰线数量 |
| `CAPTCHA_NOISE_DOTS` | `500` | 噪点数量 |
### 国际化配置 `[LANG]`
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `default_lang` | `zh-cn` | 默认语言 |
### 代码生成器配置 `[GENERATOR]`
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `PARENT_MENU_ID` | `189` | 生成模块的父菜单ID |
| `APP_NAME` | `tool` | 模块应用名(影响路由前缀和前端目录) |
| `GENERATOR_PARENT_MENU_ID` | 189 | 生成模块挂载的父菜单 ID |
| `GENERATOR_APP_NAME` | tool | 模块应用名(影响路由前缀和前端目录) |
## 多环境配置
### 开发环境
```ini
; 开发环境配置(调试开启,使用文件缓存)
APP_DEBUG = true ; 开启调试模式
APP_ENV=local
APP_DEBUG=true # 开启调试模式(异常返回真实消息)
[APP]
SHOW_ERROR_MSG = true ; 显示错误详情
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=rxthinkcmf.laravel13.elevue
DB_PREFIX=laravel_
[DATABASE]
DEBUG = true ; 显示执行的 SQL
CORS_ALLOW_ORIGIN=* # 允许所有来源(Vite 代理/直连均可用)
[CACHE]
DRIVER = file ; 使用文件缓存(无需 Redis)
CAPTCHA_BYPASS_CODE= # 可设置万能验证码(仅 debug 下生效)
```
### 生产环境
```ini
; 生产环境配置(调试关闭,使用 Redis 缓存)
APP_DEBUG = false ; 关闭调试模式
APP_ENV=production
APP_DEBUG=false # 关闭调试模式(防止堆栈泄露)
[APP]
SHOW_ERROR_MSG = false ; 不显示错误详情(防止泄露)
DB_PASSWORD=<强密码>
[DATABASE]
DEBUG = false ; 不显示 SQL
JWT_SECRET=<openssl rand -base64 32 生成的随机值> # 必须修改
[CACHE]
DRIVER = redis ; 使用 Redis 缓存
REDIS_HOST = 127.0.0.1 ; Redis 地址
REDIS_PORT = 6379 ; Redis 端口
REDIS_PASSWORD = <你的Redis密码> ; Redis 密码
CORS_ALLOW_ORIGIN=https://admin.example.com # 限制为实际域名
[JWT]
SECRET = <随机生成的强密钥> ; 签名密钥(必须修改)
[CORS]
ALLOW_ORIGIN = https://admin.example.com ; 限制为实际域名
CAPTCHA_BYPASS_CODE= # 必须留空
DEMO_MODE=false # 按需开启演示模式
TRUSTED_PROXIES= # 部署在 CDN/负载均衡后时配置
```
### 生成强密钥
```bash
# Linux / macOS:使用 OpenSSL 生成 32 字节随机 Base64 字符串
# Linux / macOS / Windows(Git Bash):OpenSSL 生成 32 字节随机 Base64 字符串
openssl rand -base64 32
# PHP:生成 32 字节随机十六进制字符串(64 个字符)
@@ -228,9 +265,21 @@ openssl rand -base64 32
php -r "echo bin2hex(random_bytes(32));"
```
## 配置缓存管理
```bash
php artisan config:clear # 清除配置缓存(修改 .env 后执行)
php artisan config:cache # 缓存配置(生产环境提升性能)
php artisan config:show jwt.secret # 查看解析后的配置值(调试用,自动脱敏)
```
:::warning
生产执行 `config:cache` 后,`.env` 的修改**不再生效**,需重新 `config:cache`。Docker 部署通过容器环境变量注入配置(优先级高于 `.env`),见 [2.8 Docker 快速体验](/zh/quickstart/docker.html)。
:::
## 前端环境变量
前端使用 Vite 的环境变量机制,配置文件位于 `ui/` 目录:
前端使用 Vite 的环境变量机制,配置文件位于前端仓库目录:
### `.env.development`(开发环境)
@@ -262,3 +311,5 @@ VITE_GLOB_API_URL_PREFIX=
// VITE_ 前缀的变量才会被暴露给客户端代码
const apiUrl = import.meta.env.VITE_GLOB_API_URL
```
详细说明见 [2.4 前端服务启动](/zh/quickstart/frontend-start.html)。
File diff suppressed because it is too large Load Diff
@@ -1,14 +1,14 @@
# 2.8 常见问题 FAQ
# 2.9 常见问题 FAQ
## 环境相关
### Q: PHP 版本低于 8.2 能运行吗?
### Q: PHP 版本低于 8.3 能运行吗?
A: 不能。项目使用了 PHP 8.2 的强类型特性(如 `readonly` 属性、`DNF` 类型等),必须使用 PHP 8.2+。
A: 不能。项目基于 Laravel 13(composer.json 要求 `php ^8.3`),必须使用 PHP 8.3+。执行 `composer install` 时 Composer 会直接校验版本并报错。
### Q: 缺少 PHP 扩展怎么办?
A: 运行 `php -m` 检查已安装的扩展。缺少的扩展参考 [2.1 节](/zh/quickstart/environment.html) 安装。最常见的是缺少 `pdo_mysql` 和 `gd`。
A: 运行 `php -m` 检查已安装的扩展。缺少的扩展参考 [2.1 开发环境准备](/zh/quickstart/environment.html) 安装。最常见的是缺少 `pdo_mysql`、`gd` 和 **`redis`**——`redis` 扩展缺失时报 `Class "Redis" not found`,验证码、登录直接不可用。
### Q: Composer 安装很慢怎么办?
@@ -23,32 +23,54 @@ composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
### Q: Redis 不安装可以吗?
A: 可以。Redis 是可选的缓存驱动,不安装时使用 file 缓存。将 `.env` 中 `CACHE.DRIVER` 设为 `file` 即可。
A: **不可以。** 本项目强依赖 Redis:验证码、JWT 双令牌会话、权限缓存、字典缓存、会话/队列驱动均依赖它。Redis 相关问题(扩展安装、密码同步)见 [2.1 开发环境准备](/zh/quickstart/environment.html) 与 [2.2 Laragon 环境搭建](/zh/quickstart/laragon.html)。
### Q: shell 里执行 php artisan 报版本过低 / 找不到类?
A: PATH 中的 `php` 可能是旧版本(Laragon 默认 PATH 常指向低版本 PHP)。使用 8.3 的完整路径调用:
```bash
$PHP_HOME\php.exe artisan list
$PHP_HOME\php.exe vendor\bin\pint --test
```
## 启动相关
### Q: `php think run` 报错 "Address already in use"
### Q: `php artisan serve` 报 "Address already in use"
A: 端口被占用,更换端口:
```bash
# --port → 指定监听端口(默认 8000)
php think run --port 8080
php artisan serve --port=8080
```
### Q: 数据库连接失败 "Connection refused"
### Q: 应用拒绝启动,报 JWT_SECRET 错误?
A: `.env` 中 `JWT_SECRET` 为空或不足 32 字符——应用启动时执行 fail-close 校验(不满足直接抛异常)。生成强随机密钥后重启:
```bash
openssl rand -base64 32 # 或 php -r "echo bin2hex(random_bytes(32));"
```
### Q: 数据库连接失败 "Connection refused" 或表不存在?
A: 检查以下几点:
1. MySQL 服务是否启动:`systemctl status mysql`
2. `.env` 中的数据库配置是否正确
3. 数据库用户是否有权限:`GRANT ALL ON \`rxthinkcmf.thinkphp6.elevue\`.* TO 'root'@'localhost'`
2. `.env` 中的 `DB_*` 配置是否正确(`DB_HOST`、`DB_PORT`、`DB_USERNAME`、`DB_PASSWORD`、`DB_DATABASE`)
3. 数据库是否已创建并导入 `document/mysql/rxthinkcmf.laravel13.elevue.sql`(本项目 migrations 为空,**不会自动建表**)
4. `DB_PREFIX` 是否为 `laravel_`(漏配会报 `Table 'xxx.users' doesn't exist`)
5. 原生测试连接:`mysql -h 127.0.0.1 -P 3306 -u root -p -e "SELECT 1;"`
### Q: 登录返回 401
A: 可能原因:
1. Token 过期 → 重新登录
1. Token 过期 → 重新登录(access_token 默认 2 小时,refresh_token 7 天)
2. 请求头格式错误 → 确保是 `Authorization: Bearer <token>`
3. JWT 密钥不匹配 → 检查 `.env` 中的 `JWT.SECRET`
3. `JWT_SECRET` 变更 → 旧令牌全部失效,重新登录即可
4. Redis 中会话被清除(如重启 Redis、执行 `FLUSHDB`)→ 重新登录
## 前端相关
@@ -65,40 +87,78 @@ pnpm install # 重新安装所有依赖
### Q: 前端请求后端 404
A: 检查以下几点:
1. 后端服务是否启动
2. Vite 代理配置是否正确(`vite.config.ts` 中的 `target`)
1. 后端服务是否启动(`curl http://localhost:8000/api/captcha` 验证)
2. Vite 代理配置是否正确(`vite.config.ts` 中的 `target`,改完重启 `pnpm dev`)
3. API 路径是否正确(应以 `/api` 开头)
### Q: 页面白屏
A: 打开浏览器控制台(F12)查看错误信息,常见原因:
1. JS 加载失败 → 检查网络请求
2. 路由配置错误 → 检查后端菜单数据
2. 路由配置错误 → 检查后端菜单数据(`/api/index/getMenus` 是否正常返回)
3. Token 过期 → 重新登录
## Oracle 相关
## 数据库初始化相关
### Q: ThinkPHP 连接 Oracle 需要哪个 PHP 扩展?
### Q: `composer setup` / `php artisan migrate` 后依然提示表不存在?
A: 需要 `pdo_oci` 扩展。ThinkPHP 的 Oracle 连接器通过 PDO 连接数据库(DSN 前缀 `oci:dbname=`),XAMPP 8.2 已自带 `php_pdo_oci.dll`,只需在 php.ini 中启用即可。
A: 正常现象。本项目 `database/migrations/` 为空,**建表以 SQL 脚本为准**,`migrate` 只初始化迁移记录表。请手动导入:
### Q: XAMPP 下 pdo_oci 扩展加载失败怎么办?
```bash
mysql -uroot -proot rxthinkcmf.laravel13.elevue < document/mysql/rxthinkcmf.laravel13.elevue.sql
```
A: 按以下顺序排查:
1. 是否安装了 Oracle Instant Client 19c(x64)并解压到固定路径
2. 该路径是否已添加到**系统变量** PATH 中(不是用户变量)
3. 是否安装了 VC++ 2015-2022 Redistributable(x64)
4. `php.ini` 中 `extension=pdo_oci` 是否已去掉分号
5. XAMPP 是否**完全关闭再启动**(不是只点 Apache 的 Start/Stop)
详见 [2.5 数据库初始化](/zh/quickstart/database-init.html)。
### Q: 命令行 `php -m` 能看到 pdo_oci,但浏览器 phpinfo() 里找不到?
### Q: 导入 SQL 很慢或中断?
A: Apache 作为 Windows 服务运行时,读取的是服务启动时的系统 PATH。如果在 Apache 启动**之后**才修改了 PATH 环境变量,Apache 不会感知到。解决方法:在 XAMPP 控制面板中完全停止 Apache,再重新启动。
A: 脚本约 226MB(含全国行政区划等大数据量表),导入需 1~3 分钟属正常。确保:
### Q: Oracle Instant Client 应该下载哪个版本?
1. 数据库字符集为 `utf8mb4`
2. 命令行窗口保持打开直到导入完成
3. MySQL 的 `max_allowed_packet` 足够(默认 64MB 一般够用,不足时调大)
A: 推荐下载 **19c** 的 **Basic Package**(x64)。下载地址:https://www.oracle.com/database/technologies/instant-client/downloads.html
## Docker 相关
### Q: 为什么 Instant Client 必须解压到固定路径?
### Q: Docker 启动后 mysql 一直 unhealthy?
A: 因为系统 PATH 环境变量写入的是绝对路径。如果解压到桌面或下载文件夹,后续清理、移动或重命名都会导致 PATH 失效,PHP 无法加载 Oracle 客户端库。推荐路径:`C:\oracle\instantclient_19_20`。
A: 首次启动需导入约 226MB SQL,健康检查已预留缓冲(1~3 分钟)。查看进度:
```bash
docker compose logs -f mysql
```
详见 [2.8 Docker 快速体验](/zh/quickstart/docker.html)。
### Q: Docker 启动即退出,日志报 JWT_SECRET?
A: 容器内读取的 `JWT_SECRET` 不足 32 字符。在 `docker/.env.docker` 中生成随机值后重启:
```bash
openssl rand -base64 32
docker compose restart app
```
### Q: 本机 Laragon 与 Docker 端口冲突?
A: 修改 `docker/.env.docker` 中的宿主机端口(如 `MYSQL_PORT=3307`、`REDIS_PORT=6380`),容器内部通过服务名通信不受影响。见 [2.8 Docker 快速体验](/zh/quickstart/docker.html) 「端口冲突处理」。
## 认证与权限相关
### Q: 接口返回 `{"code":403,"msg":"无访问权限"}`?
A: 当前账号角色缺少该接口的权限节点。到「系统管理 → 角色管理」为角色勾选对应菜单/按钮权限(权限码如 `sys:user:add`),或使用超级管理员账号测试。权限模型见 [5.4 权限管理(RBAC)](/zh/core/rbac.html)。
### Q: 接口返回 `{"code":401,"msg":"未授权,请先登录"}`?
A: Token 缺失、无效或过期。检查:
1. 请求头是否携带 `Authorization: Bearer <token>`
2. access_token 是否过期(默认 2 小时),过期后调 `/api/refresh` 续签或重新登录
3. 是否被踢下线 / 改密后全端失效(Redis 会话已清除)
### Q: 接口都是 HTTP 200,怎么判断成功失败?
A: 项目约定 HTTP 状态码恒为 200(403 例外),以响应体 `code` 判断:`code=0` 成功,其他为失败;`code=401` 时前端跳转登录。见 [8.1 统一响应格式](/zh/api-spec/response.html)。
@@ -1,7 +1,7 @@
# 2.6 第一个接口调试
# 2.7 第一个接口调试
:::tip 概述
本章通过实际操作,带您完成第一个 API 接口的调试,从登录获取 Token 到调用业务接口。
本章通过实际操作,带您完成第一个 API 接口的调试,从登录获取 Token 到调用业务接口。所有请求参数与响应字段均以本项目实际接口为准。
:::
## 调试工具
@@ -27,13 +27,17 @@ curl http://localhost:8000/api/captcha
"ok": true,
"msg": "操作成功",
"data": {
"key": "a1b2c3d4e5f6...",
"image": "data:image/png;base64,iVBOR..."
"key": "5c62cae9-3bdb-4356-bc4a-c4c61b7c3bea",
"captcha": "data:image/png;base64,iVBORw0KGgoAAAANSUh..."
}
}
```
将 `key` 保存备用,`image` 是 Base64 编码的验证码图片。
将 `key` 保存备用,`captcha` 是 Base64 编码的验证码图片(肉眼读取图片上的字符)。
:::tip
验证码存储在 Redis 中,有效期 `CAPTCHA_EXPIRE`(默认 300 秒),超过有效期需重新获取。接口同时验证了 PHP → Redis 链路,此步失败优先排查 Redis(见 [2.9 常见问题 FAQ](/zh/quickstart/faq.html))。
:::
## 步骤二:登录获取 Token
@@ -44,17 +48,17 @@ curl http://localhost:8000/api/captcha
# -X POST → 使用 POST 方法
# -H "Content-Type: ..." → 告诉服务器请求体是 JSON 格式
# -d '{...}' → 发送 JSON 请求体
# username → 用户名
# password → 密码
# captchaKey → 步骤一返回的验证码 key
# captchaCode → 验证码图片上显示的字符
# username → 用户名
# password → 密码
# key → 步骤一返回的验证码 key(与 captcha 响应字段命名一致)
# code → 验证码图片上显示的字符
curl -X POST http://localhost:8000/api/login \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"password": "123456",
"captchaKey": "a1b2c3d4e5f6...",
"captchaCode": "1234"
"key": "5c62cae9-3bdb-4356-bc4a-c4c61b7c3bea",
"code": "1234"
}'
```
@@ -80,7 +84,11 @@ curl -X POST http://localhost:8000/api/login \
}
```
将 `access_token` 保存备用。
将 `access_token` 保存备用。登录同时会写入登录日志(IP、归属地、浏览器信息),可在「系统管理 → 日志管理 → 登录日志」查看。
:::warning
验证码参数名为 `key` / `code`(与 `/api/captcha` 响应字段命名保持一致),**不是** `captchaKey` / `captchaCode`。验证码错误时返回 `{"code":1,"msg":"验证码错误"}`。
:::
## 步骤三:调用业务接口
@@ -92,11 +100,11 @@ curl -X POST http://localhost:8000/api/login \
# -----------------------------------------------------------------------------
# pageNo=1\&pageSize=10 → 分页参数(\& 是 bash 转义,防止 & 被解释为后台运行符)
# -H "Authorization: Bearer <token>" → 在请求头中携带 JWT Token
curl http://localhost:8000/api/user/page?pageNo=1\&pageSize=10 \
curl "http://localhost:8000/api/user/page?pageNo=1&pageSize=10" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
```
响应示例:
响应示例(分页字段固定为 `records / total / current / size / pages`,键名已自动转驼峰):
```json
{
@@ -115,8 +123,8 @@ curl http://localhost:8000/api/user/page?pageNo=1\&pageSize=10 \
}
],
"total": 1,
"size": 10,
"current": 1,
"size": 10,
"pages": 1
}
}
@@ -144,11 +152,18 @@ curl http://localhost:8000/api/dict/item/getDictItemList/gender \
-H "Authorization: Bearer <your_token>"
# -----------------------------------------------------------------------------
# 刷新 Token(access_token 过期后,用 refresh_token 换取新的)
# 刷新 Token(access_token 过期后,用 refresh_token 换取新的令牌对)
# -----------------------------------------------------------------------------
curl -X POST http://localhost:8000/api/oauth2/token \
# refresh 为公开接口(无需 access_token)
curl -X POST http://localhost:8000/api/refresh \
-H "Content-Type: application/json" \
-d '{"grant_type": "refresh_token", "refresh_token": "<your_refresh_token>"}'
-d '{"refresh_token": "<your_refresh_token>"}'
# -----------------------------------------------------------------------------
# 登出(令牌即时失效,需携带 access_token)
# -----------------------------------------------------------------------------
curl -X POST http://localhost:8000/api/logout \
-H "Authorization: Bearer <your_token>"
```
## 错误响应示例
@@ -159,23 +174,25 @@ curl -X POST http://localhost:8000/api/oauth2/token \
{
"code": 401,
"ok": false,
"msg": "未提供认证令牌",
"msg": "未授权,请先登录",
"data": null
}
```
HTTP 状态码为 200,前端据 body `code=401` 触发跳转登录。
### 403 禁止访问(权限不足)
```json
{
"code": 403,
"ok": false,
"msg": "无访问权限:用户列表",
"msg": "无访问权限",
"data": null
}
```
### 422 参数验证失败
### 参数验证失败(FormRequest,code=1)
```json
{
@@ -183,11 +200,27 @@ curl -X POST http://localhost:8000/api/oauth2/token \
"ok": false,
"msg": "参数验证失败",
"data": {
"username": "用户名不能为空"
"username": ["用户名不能为空"],
"password": ["密码不能为空"]
}
}
```
### 业务失败(BizException,code=1)
```json
{
"code": 1,
"ok": false,
"msg": "验证码错误",
"data": null
}
```
:::tip 统一约定
所有接口 HTTP 状态码默认 200(403 例外),以响应体 `code` 判断结果:`0` 成功,其余为失败。详见 [8.1 统一响应格式](/zh/api-spec/response.html) 与 [5.16 统一响应与异常抛出](/zh/core/exception.html)。
:::
## ApiFox 配置建议
1. 创建环境,设置变量:
@@ -198,11 +231,12 @@ curl -X POST http://localhost:8000/api/oauth2/token \
- `Authorization` = `Bearer {{token}}`
3. 登录请求的「后置操作」中添加脚本自动保存 Token:
```javascript
// 解析响应 JSON
var response = pm.response.json();
// 登录成功时自动将 access_token 保存到环境变量
if (response.code === 0) {
pm.environment.set("token", response.data.access_token);
}
```
```javascript
// 解析响应 JSON
var response = pm.response.json();
// 登录成功时自动将 access_token 保存到环境变量
if (response.code === 0) {
pm.environment.set("token", response.data.access_token);
}
```
@@ -1,13 +1,13 @@
# 2.3 前端服务启动
# 2.4 前端服务启动
:::tip 概述
本章指导您启动前端开发服务器,包括依赖安装、环境配置和 Vite 开发服务器启动。
本章指导您启动前端开发服务器,包括依赖安装、环境配置和 Vite 开发服务器启动。前端为本仓库 `ui/` 目录内的 EleVue 管理后台(Vue3 + ElementPlus)。
:::
## 步骤一:安装依赖
```bash
# 进入前端项目目录
# 进入前端项目目录(克隆/解压前端仓库至 ui/ 目录)
cd ui
# -----------------------------------------------------------------------------
@@ -21,7 +21,17 @@ npm install
```
:::warning Node.js 版本
前端项目要求 Node.js >= 18,推荐使用 22.x。可通过 `node -v` 检查版本。
前端项目要求 Node.js >= 22(推荐 22.18.0)。可通过 `node -v` 检查版本,安装方式见 [2.1 开发环境准备](/zh/quickstart/environment.html)。
:::
:::tip 国内镜像
安装速度较慢时配置镜像源:
```bash
pnpm config set registry https://registry.npmmirror.com
# 或
npm config set registry https://registry.npmmirror.com
```
:::
## 步骤二:配置环境变量
@@ -38,7 +48,7 @@ VITE_PORT = 8001
VITE_PUBLIC_PATH = /
# 代理配置(JSON 数组格式:[["前缀","目标地址"]])
VITE_PROXY = [["/api","http://admin.thinkphp6.elevue/api"]]
VITE_PROXY = [["/api","http://localhost:8000/api"]]
# API 接口基础路径(留空,由代理处理)
VITE_GLOB_API_URL =
@@ -49,8 +59,8 @@ VITE_GLOB_API_URL_PREFIX = /api
# 上传接口地址
VITE_GLOB_UPLOAD_URL = /api/upload
# 图片访问地址
VITE_GLOB_IMG_URL = http://file.thinkphp6.elevue
# 图片访问地址(附件域名,对应后端 FILE_DOMAIN_URL)
VITE_GLOB_IMG_URL = http://file.laravel13.elevue
# 是否启用 Mock 数据(false = 使用真实后端 API)
VITE_USE_MOCK = false
@@ -75,7 +85,7 @@ VITE_GLOB_API_URL_PREFIX = /api
VITE_GLOB_UPLOAD_URL = /api/upload
# 图片访问地址
VITE_GLOB_IMG_URL = http://file.thinkphp6.elevue
VITE_GLOB_IMG_URL = https://file.example.com
# 是否启用 gzip 压缩
VITE_BUILD_COMPRESS = gzip
@@ -114,18 +124,24 @@ npm run dev
```typescript
// ui/vite.config.ts
server: {
port: 8001, // 开发服务器端口(对应 VITE_PORT)
port: 8001, // 开发服务器端口(对应 VITE_PORT)
proxy: {
'/api': {
target: 'http://admin.thinkphp6.elevue/api', // 后端服务地址(对应 VITE_PROXY)
changeOrigin: true, // 开启跨域(修改请求头的 Origin)
rewrite: (path) => path, // 保持路径不变(不做路径替换)
target: 'http://localhost:8000/api', // 后端服务地址(对应 VITE_PROXY)
changeOrigin: true, // 开启跨域(修改请求头的 Origin)
rewrite: (path) => path, // 保持路径不变(不做路径替换)
},
},
}
```
确保后端服务已启动(`php think run`),然后访问 `http://localhost:8001`。
后端使用虚拟域名时(见 [2.2 Laragon 环境搭建](/zh/quickstart/laragon.html)),target 改为对应地址:
```typescript
target: 'http://admin.laravel13.elevue/api',
```
确保后端服务已启动(`php artisan serve`),然后访问 `http://localhost:8001`。
## 步骤五:验证前后端联调
@@ -135,7 +151,9 @@ server: {
4. 登录成功后进入控制台页面
:::tip 代理原理
浏览器 → `http://localhost:8001/api/login` → Vite 代理 → `http://admin.thinkphp6.elevue/api/login` → 后端处理
浏览器 → `http://localhost:8001/api/login` → Vite 代理 → `http://localhost:8000/api/login` → 后端处理
代理方式下前端无需关心跨域;若改为直连后端(`VITE_GLOB_API_URL` 配置完整地址),则依赖后端 CORS 配置(`.env` 中 `CORS_ALLOW_ORIGIN`)。
:::
## 构建生产版本
@@ -153,7 +171,11 @@ pnpm build
pnpm preview
```
构建完成后,`ui/dist/` 目录包含可部署的静态文件。
构建完成后,`ui/dist/` 目录包含可部署的静态文件,部署方式见 [11.5 前端部署](/zh/deploy/frontend.html)。
:::tip
后端仓库根目录的 `composer dev` / `composer setup` 也会执行 `npm run build`(构建后端 `resources/` 下的静态资源),与前端仓库的构建相互独立。
:::
## 常见问题
@@ -183,9 +205,15 @@ server: {
检查后端服务是否启动,以及代理配置是否正确:
```bash
# 确认后端在运行(直接请求后端登录接口)
# -X POST → POST 方法
# -H → 设置 Content-Type 为 JSON
# -d → 发送 JSON 请求体
curl http://admin.thinkphp6.elevue/api/login -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":"123456"}'
# 确认后端在运行(直接请求后端验证码接口)
curl http://localhost:8000/api/captcha
# → {"code":0,...} 即后端正常
```
再检查 `vite.config.ts` 中 `proxy.target` 是否指向后端实际地址(改完需重启 `pnpm dev`)。
### 4. 登录报 401
1. Token 过期 → 重新登录(access_token 默认 2 小时)
2. 请求头格式错误 → 确保是 `Authorization: Bearer <token>`
3. 后端 `JWT_SECRET` 变更 → 旧令牌全部失效,重新登录即可
@@ -0,0 +1,294 @@
# 2.2 Laragon 环境搭建
:::tip 概述
本项目推荐使用 Laragon 在 Windows 下搭建本地开发环境(Apache + PHP 8.3 + MySQL + Redis)。本页基于实际部署记录整理,涵盖 Laragon 目录结构、hosts 域名解析、Apache 虚拟主机配置、phpredis 扩展安装、php.ini 关键配置、Redis 密码与故障排查。完成本页后,即可进入 [2.3 后端服务启动](/zh/quickstart/backend-start.html)。
:::
## 环境概况
| 项目 | 说明 |
|------|------|
| 操作系统 | Windows 10 / 11 |
| 集成环境 | Laragon(如 `D:\laragon`) |
| Web 服务器 | Apache 2.4(Laragon 内置,mod_php 方式) |
| PHP | 8.3.x Win64 **TS(线程安全)**,项目要求 ≥ 8.3 |
| 数据库 | MySQL 8.x(Laragon 内置) |
| 缓存 | Redis(Laragon 内置,`127.0.0.1:6379`) |
**关键目录:**
```text
D:\laragon\www\ # 站点根目录(Apache 主 DocumentRoot)
D:\laragon\bin\apache\httpd-2.4.x-...\ # Apache 主目录(下称 $HTTPD_HOME)
D:\laragon\bin\php\php-8.3.x-Win32-vs16-x64\ # PHP 8.3 目录(下称 $PHP_HOME)
D:\laragon\www\RXThinkCMF_Laravel13_EleVue\ # 项目根目录(下称 $PROJECT)
```
:::warning PATH 中的 php 版本陷阱
Laragon 默认 shell PATH 指向的可能是旧版 PHP(如 7.3),直接执行 `php artisan` 会报 `requires PHP >= 8.3`。执行 artisan、pint、composer 时**必须使用 8.3 的完整路径**:
```bash
$PHP_HOME\php.exe artisan list
$PHP_HOME\php.exe vendor\bin\pint --test
```
:::
## 安装 PHP 8.3
1. 打开 Laragon 菜单 **PHP → Version**,选择 8.3.x;
2. 列表中没有 8.3 时,手动下载 `php-8.3.x-Win32-vs16-x64.zip`(**TS 线程安全版**,Laragon 的 Apache 以 mod_php 方式运行,必须用 TS),解压到 `D:\laragon\bin\php\` 下,重启 Laragon 后重新选择;
3. 验证版本与线程安全类型:
```bash
$PHP_HOME\php.exe -v
# PHP 8.3.x (ts) ...
$PHP_HOME\php.exe -i | findstr "Thread Safety"
# Thread Safety => enabled ← TS(Apache mod_php 需要)
$PHP_HOME\php.exe -i | findstr "extension_dir"
# extension_dir => D:/laragon/bin/php/php-8.3.x-Win32-vs16-x64/ext ← 记下扩展目录
```
## hosts 域名解析
编辑 `C:\Windows\System32\drivers\etc\hosts`(管理员权限):
```text
127.0.0.1 admin.laravel13.elevue file.laravel13.elevue
```
| 域名 | 用途 |
|------|------|
| `admin.laravel13.elevue` | 应用入口(DocumentRoot 指向项目 `public/`) |
| `file.laravel13.elevue` | 附件目录(DocumentRoot 指向项目 `uploads/`) |
同系列项目域名格式为 `admin.<项目>.<前端品牌>`,如 `admin.laravel13.antdvue`、`admin.laravel13.naivevue`。
## Apache 虚拟主机配置
### 配置位置
`httpd.conf` 末尾有两处 Include,均可放置虚拟主机:
```apache
IncludeOptional "D:/laragon/etc/apache2/sites-enabled/*.conf" # Laragon 自动生成
Include "conf/vhost/*.conf" # 手动配置(本项目在此)
```
本项目使用 `$HTTPD_HOME\conf\vhost\laravel13.conf`。
### 配置模板(Directory 块缺一不可)
```apache
# 项目应用本地虚拟主机配置
<VirtualHost *:80>
ServerAdmin rxthinkcmf@163.com
ServerName admin.laravel13.elevue
DocumentRoot "D:\laragon\www\RXThinkCMF_Laravel13_EleVue\public"
ErrorLog "logs/laravel13-error.log"
CustomLog "logs/laravel13-access.log" common
<Directory "D:\laragon\www\RXThinkCMF_Laravel13_EleVue\public">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
# 项目附件目录本地虚拟主机配置
<VirtualHost *:80>
ServerName file.laravel13.elevue
DocumentRoot "D:\laragon\www\RXThinkCMF_Laravel13_EleVue\uploads"
ErrorLog "logs/laravel13-error.log"
CustomLog "logs/laravel13-access.log" common
<Directory "D:\laragon\www\RXThinkCMF_Laravel13_EleVue\uploads">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
```
### 为什么 Directory 块必须写(404 根因)
主 `httpd.conf` 中的默认设置为 `AllowOverride None`(全局忽略 `.htaccess`)。Apache 的 `<Directory>` 写在某个 `<VirtualHost>` 内部时**只对该虚拟主机生效**,因此 vhost 必须自带 `<Directory>` 块,把本虚拟主机范围内的 `AllowOverride` 覆盖为 `All`,Laravel 的 `public/.htaccess`(URL 重写规则)才能生效。
**症状特征(识别此问题的关键):**
```text
GET / → 200 正常(DirectoryIndex 命中物理文件 index.php)
GET /api/captcha → 404,且是 Apache 原生 HTML 404 页
<title>404 Not Found</title>(不是 Laravel 的 JSON 响应)
```
解读:物理文件能访问、非物理 URL 无法重写进 `index.php`,说明请求根本没有进入 Laravel。**只要 404 页面是 Apache 原生的,问题一定在 Web 服务器配置,而不是 Laravel 路由。**
### 配置校验与重启
```bash
# 改动配置后必做:校验语法(输出 Syntax OK 即通过;AH00112 警告可忽略)
$HTTPD_HOME\bin\httpd.exe -t
# Apache 未注册为 Windows 服务,-k restart 会报 "No installed service",需手动重启:
taskkill //F //IM httpd.exe
cmd //c start "" "D:\laragon\bin\apache\httpd-2.4.x-...\bin\httpd.exe"
# 验证进程(应看到 2 个 httpd.exe:父进程 + 工作进程)
tasklist //FI "IMAGENAME eq httpd.exe"
```
:::tip
Laragon 菜单 **Apache → Reload** 亦可;手动部署环境下以上命令最可靠。
:::
## 安装 phpredis 扩展
项目强依赖 Redis(验证码、JWT 会话、字典缓存),PHP 必须安装 redis 扩展,否则报 `Class "Redis" not found`。
### 判断线程安全类型(决定下载哪个包)
```bash
$PHP_HOME\php.exe -i | findstr "Thread Safety"
# Thread Safety => enabled → TS 包(Laragon 的 Apache 用 TS)
# Thread Safety => disabled → NTS 包(php-fpm 用)
```
### 下载
官方 Windows 扩展仓库(`windows.php.net` 会 302 跳转到 `downloads.php.net`):
```text
https://downloads.php.net/~windows/pecl/releases/redis/
# 目录按版本号分子目录:6.0.0/ 6.1.0/ 6.2.0/ 6.3.0/ ...
```
文件名命名规则(**四个要素必须全部匹配**):
```text
php_redis-{扩展版本}-php-{PHP主次版本}-{ts|nts}-vs16-{x64|x86}.zip
本次实际下载(PHP 8.3 + TS + 64 位 + VS16 编译器):
https://downloads.php.net/~windows/pecl/releases/redis/6.3.0/php_redis-6.3.0-8.3-ts-vs16-x64.zip
```
### 安装
```bash
# 解压后将 php_redis.dll 复制到扩展目录
copy php_redis.dll D:\laragon\bin\php\php-8.3.x-Win32-vs16-x64\ext\
```
### php.ini 配置
编辑 `$PHP_HOME\php.ini`(mod_php 与 CLI 共用同一份),在扩展区按字母序新增一行:
```ini
extension_dir = "D:/laragon/bin/php/php-8.3.x-Win32-vs16-x64/ext"
...
;extension=pgsql
extension=redis ← 新增此行(PHP 8 写法:不带 php_ 前缀和 .dll 后缀)
;extension=shmop
```
:::danger 常见坑
- 写成 `extension=php_redis.dll` 是 PHP 5 时代的旧写法,PHP 8 下报错;
- 只改 `php.ini-development` / `php.ini-production` 无效,PHP 加载的是 `php.ini`;
- **改完必须重启 Apache**(mod_php 常驻进程,不会热加载)。
:::
### 验证
```bash
# CLI 验证(应输出 redis)
$PHP_HOME\php.exe -m | findstr redis
# HTTP 验证(重启 Apache 后调用依赖 Redis 的接口,见下方「部署验证清单」)
```
## php.ini 关键配置
`$PHP_HOME\php.ini` 中与本项目相关的关键配置:
| 配置项 | 建议值 | 说明 |
|--------|--------|------|
| `extension` 系列 | 按需启用 | 项目必需:`pdo_mysql`、`mbstring`、`gd`、`bcmath`、`zip`、`redis`、`openssl`、`fileinfo`、`curl`;跨库迁移目标为 PostgreSQL 时另启用 `pdo_pgsql` |
| `memory_limit` | `256M` | Excel 导出等大内存场景 |
| `max_execution_time` | `60` | 导入导出超时上限 |
| `upload_max_filesize` / `post_max_size` | `50M` | 与 `.env` 中 `FILE_MAX_SIZE` 配合(业务限制更小) |
| `date.timezone` | `Asia/Shanghai` | 与应用时区一致 |
| `opcache.enable` | `1` | 本地开发可保持 `opcache.validate_timestamps=1`,改代码即时生效 |
修改后重启 Apache 生效。
## Redis 密码配置
### 状态确认
```bash
tasklist //FI "IMAGENAME eq redis-server.exe" # 进程
netstat -an | findstr ":6379" # 应看到 127.0.0.1:6379 LISTENING
```
### 设置密码(实际踩坑记录)
:::warning
Redis 无密码直连可用;一旦在 Redis 侧设置了 `requirepass` 而 `.env` 未同步,Laravel 所有 Redis 操作(验证码、JWT 会话、限流)会全部失败,且 CaptchaService 的 catch 会把真实错误吞掉,只报「验证码生成失败 / 验证码服务不可用」。
:::
处理步骤(缺一不可):
1. Redis 服务端设置密码(`redis.conf` 的 `requirepass` 或 `CONFIG SET requirepass`);
2. `.env` 同步配置:
```ini
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=你的密码 ← 原为 null,必须改
REDIS_PORT=6379
```
3. 清除配置缓存并重启 Apache:
```bash
$PHP_HOME\php.exe artisan config:clear
```
### 单独验证 Redis 连通(绕开业务代码)
```bash
$PHP_HOME\php.exe artisan tinker --execute="try { \Illuminate\Support\Facades\Redis::setex('test:key', 10, 'ok'); echo 'OK: '.\Illuminate\Support\Facades\Redis::get('test:key'); } catch (\Throwable \$e) { echo get_class(\$e).': '.\$e->getMessage(); }"
# 输出 OK: ok 即连接正常(测试键 10 秒自动过期)
```
## 部署验证清单
按顺序全部通过即环境就绪:
```text
□ $PHP_HOME\php.exe -v → PHP 8.3.x (ts)
□ $PHP_HOME\php.exe -m | findstr redis → redis
□ httpd.exe -t → Syntax OK
□ tasklist 查看 redis-server.exe → 进程存在,6379 LISTENING
□ curl http://admin.laravel13.elevue/api/captcha
→ {"code":0,"ok":true,"msg":"操作成功","data":{...}}
□ curl -X POST http://admin.laravel13.elevue/api/login
→ {"code":1,"ok":false,"msg":"参数验证失败","data":{...}} (路由已通)
```
验证码接口返回 `code=0` 的 JSON 即部署成功——它同时验证了 Apache 重写、PHP 8.3、Redis 连通三条链路。
## 常见问题
| 症状 | 原因 | 处理 |
|------|------|------|
| `/` 正常,`/api/*` 返回 Apache 原生 HTML 404 | vhost 缺 `<Directory>` 块,`.htaccess` 被忽略 | 补 `AllowOverride All` 块,`httpd -t` 后重启 Apache |
| `Class "Redis" not found` | PHP 缺 redis 扩展 | 装 `php_redis.dll` + `php.ini` 加 `extension=redis` |
| 验证码生成失败 / 服务不可用 | Redis 密码不匹配或服务未启(日志里真实错误被 catch 吞掉) | `.env` 配 `REDIS_PASSWORD`,tinker 单测 Redis 连通 |
| artisan/pint 报 requires PHP >= 8.3 | shell PATH 的 php 是旧版 | 用 `$PHP_HOME\php.exe` 完整路径调用 |
| `httpd -k restart` 报 No installed service | Apache 未注册 Windows 服务 | `taskkill` 后重新启动 `httpd.exe` |
| 改了配置 / 扩展不生效 | mod_php 常驻不热加载 | 必须重启 Apache |
## 相关页面
- [2.1 开发环境准备](/zh/quickstart/environment.html) — 版本要求总览
- [2.3 后端服务启动](/zh/quickstart/backend-start.html) — 安装依赖、初始化项目
- [2.6 环境变量配置](/zh/quickstart/env-vars.html) — `.env` 全部配置项说明
- [2.9 常见问题 FAQ](/zh/quickstart/faq.html) — 更多启动期问题