Skip to content

部署与调度指南 ​

文档用途 ​

  • 描述当前生产环境真实的部署拓扑与调度模型
  • 对齐时间:2026-09-13
  • 全新服务器部署先看 docs/guide/deploy/bt-panel.md(宝塔面板)或 docs/guide/deploy/bare-metal.md(无面板、无容器的原生服务部署)
  • **容器化部署(Docker Compose / 1Panel / CI 自动打包)**先看 docs/guide/deploy/docker-1panel.md
  • 配套文档:
    • docs/README.md(文档入口)
    • docs/develop/local-development.md(本地启动)
    • docs/develop/architecture.md(总体架构)

1. 当前生产拓扑 ​

项目跑在单台宝塔服务器上,结构如下:

宝塔站点 (Nginx)
  ├── 官网/用户入口 -> frontend-user-v3-www/dist 静态资源
  ├── 用户控制台   -> frontend-user-v4-console/dist 静态资源
  ├── 管理端站点   -> frontend-admin-v3/dist 静态资源
  └── 后端 API 站点 -> backend/public (PHP-FPM)
                       │
                       ├── MySQL 8      (本机或独立实例)
                       ├── Redis        (缓存)
                       ├── 宝塔计划任务   每分钟执行一次 schedule:run
                       └── 宝塔守护进程   常驻 php artisan vnc:relay

生产环境特征:

  • 四端各自独立宝塔站点,浏览器从三个前端站点直接访问 API 域名;前端 Nginx 不反代 API
  • 后端入口走 PHP-FPM,不使用 php artisan serve 或 app:serve
  • 没有常驻 Queue Worker,每分钟 schedule:run 驱动三个入口:scheduler:heartbeat(唯一时钟源,按 15 分钟槽位去重派发定时任务)、queue:drain(后台子进程消费队列,按 provision、业务组 referral,notification,coupon,default、定时组 automation 三个队列组分别建 Worker,drain 锁保证同一队列不并发,空闲时自动退出)、scheduler:liveness(心跳存活探针,心跳异常时仍能告警)
  • VNC Relay 独立常驻:Relay 重启不会阻塞心跳、支付、新购或续费
  • 新购、续费支付后同步履约:支付回调内直接调用上游开通/续费,用户付款后立即生效;同步失败才回退 provision 队列,由每分钟心跳 Worker 重试,不再依赖队列轮询延迟

2. 计划任务配置 ​

2.1 宝塔计划任务 ​

在宝塔面板 → 计划任务 → 添加:

  • 任务类型:Shell 脚本
  • 执行周期:每分钟执行
  • 脚本内容:
bash
cd /你的项目/backend
php artisan schedule:run >> /dev/null 2>&1

说明:

  • 这条计划每分钟同时触发:心跳派发(15 分钟槽位去重)、队列消费(queue:drain 后台按队列组建 Worker)与存活探针;任一队列仍在消费时,drain 锁只跳过该队列的重复 Worker,其余队列组继续按分钟消费
  • 不要再配置覆盖同一队列的常驻 queue:work,避免重复消费;需要低延迟时应按队列边界单独评估
  • 任务执行超时强杀(--timeout 与 JobTimedOut 收敛)依赖 Linux pcntl/SIGALRM,Windows 开发环境不生效;超时类问题必须在 Linux 环境验证,Windows 上长任务不会被自动终止
  • 错过的 15 分钟心跳槽位默认不自动回放,需依赖任务自身幂等补偿或管理端手动触发

2.2 VNC Relay 守护 ​

在宝塔进程守护或其他进程管理器中增加常驻命令:

bash
cd /www/wwwroot/你的项目/backend
php artisan vnc:relay

上线前确认 127.0.0.1:8100 已监听;Relay 异常时由守护进程自动重启。vnc:ensure-relay 仅保留为手工健康检查/故障修复命令,不再注册到心跳任务。

2.3 后端调度内容 ​

当前调度能力以真实命令与路由注册为准。查看真实启用清单请看:

  • backend/routes/console.php
  • backend/app/Console/Commands/
  • 管理端 "调度总览":GET /api/v2/admin/schedules/overview

典型任务(以代码为准):

  • scheduler:heartbeat / queue:drain / scheduler:liveness:每分钟,调度与队列主干
  • tickets:cleanup-unused-upstream-uploads:每分钟,删除超保留期未被工单回复引用的上游上传文件
  • open-api:prune-usage-logs:每周,按 open_api.usage_log_retention_days(默认 90 天)清理 API 用量日志
  • 自动续费检查、账单逾期处理、服务到期停用与终止、上游状态同步、各类日志清理:由心跳按槽位规则派发,清单见管理端「调度总览」

3. 部署流程 ​

3.1 后端部署 ​

bash
# 项目根下切到 backend
cd backend

# 拉代码(宝塔一般自带 Git 钩子或手动 git pull)
git pull

# 安装依赖
composer install --no-dev --optimize-autoloader

# 数据库初始化/更新
# 新环境首次部署:使用 schema baseline
python scripts/install_db.py
# 后续增量更新:
php artisan migrate --force

# 清旧缓存
php artisan config:clear
php artisan route:clear
php artisan cache:clear

# 生产缓存
php artisan config:cache
php artisan route:cache

数据库初始化说明:

  • database/schema/mysql-schema.sql 是当前生产库完整结构快照,包含 62 张表和 migrations 记录
  • 历史迁移文件已归档至 database/migrations/_archive/,不再逐个执行
  • 增量迁移(database/migrations/*.php)用于后续新功能上线
  • install_db.py 最后会经 SettingsSeeder::seed() 幂等种入:
    • 系统核心默认配置(settings 表,含 notification.email_enabled / sms_enabled / sms_template_code)
    • 通知模板默认数据(notification_templates 表):email 全量模板 + sms 验证码模板
    • schema baseline 仅导出表结构、不含模板数据,模板种子必须经该 Seeder 执行,切勿依赖迁移或手工补种

首次部署后请检查(管理后台 → 站点设置 → 通知总开关):

  • 邮件通知 / 短信通知默认关闭,按需在后台打开
  • 邮件/短信插件需在「插件管理」安装、配置并绑定,再前往「通知与接口」维护对应模板

3.2 前端部署 ​

bash
# 项目根目录:只安装一次 workspace 依赖(pnpm monorepo,四个 workspace)。
# --shamefully-hoist:多个 vite build(尤其 www 的 element-plus 深路径 import)
# 会从根 node_modules 解析依赖,必须扁平提升,否则构建报 load-fallback ENOENT。
# verify-deps-before-run=false:关闭 pnpm run 前的依赖检查,避免它重新 purge 掉 hoist 布局。
pnpm install --frozen-lockfile --shamefully-hoist
pnpm config set verify-deps-before-run false

# 每个前端构建时读取各自目录下的 .env 获取后端/前端地址,
# 构建三个完全独立的 dist,不复制任何文件到 backend/public。
pnpm run build:frontends
# 若在无 TTY 的 CI/脚本环境跑 build,需 CI=true(跳过 pnpm 的模块目录移除交互确认)。
CI=true pnpm run build:frontends

产物目录分别是 frontend-user-v3-www/dist、frontend-user-v4-console/dist、frontend-admin-v3/dist,各自指向对应宝塔站点根目录。

VitePress 文档官网不属于上述三端,必须通过 pnpm run build:docs 独立构建和发布。内容维护、产物目录与发布检查见文档官网维护指南。

dev 与 build 的 env 分开:本地开发(pnpm run dev)读取各前端目录下的 .env.dev(dev 脚本用 --mode dev);构建(pnpm run build:frontends)读取各前端目录下的 .env。前端构建不依赖 backend 环境文件。

每个前端目录(frontend-admin-v3、frontend-user-v3-www、frontend-user-v4-console)需要两份 env(均 gitignore,不入库):

  • .env.dev:本地开发地址(127.0.0.1 各端端口、本地 API)
  • .env:构建地址(生产域名),部署打包前填写真实域名

部署前确认 .env 中 VITE_API_BASE_URL、VITE_PUBLIC_SITE_URL、VITE_CONSOLE_SITE_URL、VITE_SESSION_COOKIE_DOMAIN 与生产一致;缺项或填错会让产物指向错误地址。

站点地址的真源是 backend/.env 的四个公开地址:backend/scripts/build_frontends.mjs 会把它们解析成 VITE_* 注入构建,--print-env 可单独打印这套变量供其他构建入口复用。这样各前端目录下的 .env 只是本地便利,不会与线上配置漂移。

构建配置来源的区别:

  • 直接执行前端构建(pnpm run build):各前端目录读取自身目录下的 .env(或 .env.production),仅用该目录内的配置。
  • 通过 pnpm run build:frontends 统一构建:由 backend/scripts/build_frontends.mjs 读取 backend/.env 中的四个公开地址,解析后作为 VITE_* 环境变量注入到各前端构建中。这样各前端目录下的 .env 只是本地便利,不会与线上配置漂移。

backend/scripts/build_frontends.mjs --print-env 可单独打印这套变量,供其他构建入口(如 deploy/deploy-www.sh 的 inject_backend_site_env)复用,保证所有构建入口读到同一份站点地址真源,避免与实际入口行为矛盾。

bash
# 只预览构建目标,不实际构建
pnpm run build:frontends:dry

# 只打印从 backend/.env 解析出的 VITE_* 站点地址(供部署脚本复用)
node backend/scripts/build_frontends.mjs --print-env

3.2.1 单域名子路径部署下的控制台地址 ​

现网把官网、控制台、管理端、API 合并到同一个域名,用子路径区分:

端访问路径对应 backend/.env
官网/FRONTEND_URL=https://你的域名
控制台/console/CLIENT_CONSOLE_URL=https://你的域名/console
管理端/admin/ADMIN_URL=https://你的域名/admin
API/apiAPP_URL=https://你的域名

单域名部署时 VITE_CONSOLE_SITE_URL 必须带 /console 子路径。控制台的 router base 就是 /console/,写成不带子路径的裸域名会让官网的「登录 / 免费注册」跳到 https://你的域名/client/login——那条路径落在官网 SPA 上,永远到不了控制台登录页。

为此 frontend-user-v3-www/vite.config.js 加了构建守卫:VITE_CONSOLE_SITE_URL 留空,或与 VITE_PUBLIC_SITE_URL 同源却没有子路径时,pnpm build 直接失败。控制台地址配错会在构建期暴露,不会带着坏链接发版。

单域名部署时 CLIENT_SESSION_COOKIE_DOMAIN 建议留空(登录态本来就在同源);需要跨子域共享登录态时才填父域。

CLIENT_CONSOLE_URL 必须带 /console:后端 AuthService::normalizeConfiguredUrl() 允许地址带路径(单域名部署的前提),管理员代登录链接就是在该地址后追加 /client/login-as。如果把它写成不带子路径的裸域名,官网的登录/注册会跳到 https://你的域名/client/login(官网 SPA),代登录也会落到官网页面上。 管理端会把后端下发的地址再拼一次 /client/login-as,两边都按「base path 保留」处理。

3.3 .env 关键项 ​

最小化需要核对的 .env:

  • APP_ENV=production
  • APP_DEBUG=false
  • APP_URL=https://api.你的域名(API、/media/*、/uploads/*、VNC WebSocket)
  • FRONTEND_URL=https://www.你的域名(单域名子路径部署写 https://你的域名,见 3.2.1)
  • CLIENT_CONSOLE_URL=https://console.你的域名(单域名子路径部署写 https://你的域名/console)
  • ADMIN_URL=https://admin.你的域名(单域名子路径部署写 https://你的域名/admin)
  • CLIENT_SESSION_COOKIE_DOMAIN=.你的域名(官网和控制台需要跨子域共享登录态时设置;本地留空)
  • SESSION_SECURE_COOKIE=true(HTTP 环境改为 false)
  • DB_*:MySQL 8 连接
  • REDIS_*:Redis 连接
  • CACHE_STORE=redis
  • SESSION_DRIVER=file(当前生产口径)
  • QUEUE_CONNECTION=database(当前生产口径)
  • TURAIDC_PROVISION_QUEUES=provision(开通队列组,Worker 超时可达 1200 秒)
  • TURAIDC_BUSINESS_QUEUES=referral,notification,coupon,default(业务队列组;若存量 .env 仍含 provision,运行时会自动剔除,不会重复消费)
  • TURAIDC_SCHEDULE_QUEUE=automation(定时任务队列组)

四个公开地址可统一使用 HTTP 或 HTTPS;HTTPS 前端不能指向 HTTP API。CORS 会从三个前端 URL 自动生成精确 allowlist,无需在前端 Nginx 增加代理。

Redis 当前用于缓存;不要在没有专项方案和验证的情况下把会话或队列切到 Redis。

3.4 受信代理与来源 IP 契约 ​

后端在 backend/bootstrap/app.php 配置了 trustProxies:只信任回环与 RFC1918/ULA 私网段,且只采信 X-Forwarded-For、X-Forwarded-Port、X-Forwarded-Proto(不信任 X-Forwarded-Host)。request()->ip() 依赖该配置解析真实客户端 IP,直接影响:

  • 审计字段(如 last_login_ip)与推荐风控的“注册 IP == 推荐人 IP”判断;
  • /upload_image 上游附件上传的启用开关、白名单/限流中间件(TicketUpstreamUploadThrottle)。接口默认关闭,管理端配置工单传递规则前必须先开启;白名单外上传默认拒绝。

部署必须满足以下契约,否则 IP 白名单与限流会被绕过或误判:

  1. 边缘反向代理是唯一公网入口:后端 API 端口(宝塔 PHP-FPM 站点 / 容器 8080)只能被受信反代访问,不能直接暴露给公网或不可信私网来源。
  2. 代理必须真实生成来源头:外部反代需携带 X-Forwarded-For、X-Forwarded-Proto。单层受信代理应把 X-Forwarded-For 重置为 $remote_addr,不要用 $proxy_add_x_forwarded_for 原样追加——否则客户端可自带伪造 XFF 直接污染 request()->ip()。只有明确存在多层受信代理且每层都清洗时才允许追加。
  3. 不得把公网代理地址加入 trustProxies:当前配置只信任回环与私网段;若实际入口代理不在这些网段(例如托管在公有云的 LB),必须先评估暴露面并收窄为明确的入口 IP/CIDR,再修改配置。
  4. 宝塔 PHP-FPM 直连形态(前端 Nginx 不反代 API)本身不产生 XFF;后端以 PHP-FPM 接收到的 REMOTE_ADDR 为准,不受上述契约影响,但也不能依赖 XFF 取客户端 IP。

容器部署注意:deploy/docker/.env.example 默认把 API_PORT=8080 暴露到所有网卡。上线前应改为仅本机绑定(127.0.0.1:8080),由宿主机 Nginx/1Panel 反代进入;否则私网来源可直接访问 API 并伪造 XFF。

3.5 增量更新的实测坑 ​

以下是现网增量更新(git pull + 重建 dist)时真实踩过的问题。

3.5.1 前端必须走 pnpm run build:frontends,不能逐个 pnpm run build ​

逐个构建读的是各前端目录自己的 .env / .env.production,而项目真源是 backend/.env 的四个公开地址(由 build_frontends.mjs 注入)。两处一旦不同步,产物里 canonical、og:url、结算回跳地址就会全部写成前端目录里的旧值。

实测过:官网 .env.production 是 idc.25y.cn,而 backend/.env 的 FRONTEND_URL 是 next2.25y.cn。逐个构建后产物里 idc.25y.cn 出现 4 处、next2.25y.cn 0 处—— 站点 HTTP 全 200,但 SEO 与回跳地址全是错的。改用标准脚本重建后归零。

部署后按真源核对,不要只看能不能访问:

bash
grep -ro "$FRONTEND_HOST" frontend-user-v3-www/dist/ | wc -l   # 应 > 0
grep -ro "<其他域名>"   frontend-user-v3-www/dist/ | wc -l       # 应为 0

3.5.2 生产机没有 pnpm,标准脚本会 ENOENT ​

build_frontends.mjs 内部 spawn pnpm。若生产 PATH 里只有 node 与 corepack (packageManager 指定了 pnpm 版本、corepack 缓存里也有,但没 enable 过), pnpm run build:frontends 会以 ENOENT: spawn pnpm 失败。

一次性修复:

bash
corepack enable pnpm   # 生成 /usr/bin/pnpm shim
pnpm -v                # 确认版本与 packageManager 一致

在 corepack enable 之前只能用 corepack pnpm run build 逐个构建,但那正是 3.5.1 说的 会绕过地址注入的方式,不要当成常态。

3.5.3 git pull 会被 untracked 文件阻塞 ​

为紧急止血手工放进 backend/public/media/console-panel/ 的静态资产,与后续提交里的同名路径 冲突,pull 直接拒绝:

error: Untracked working tree files ... Please move or remove them before you merge.

先比对内容再删工作区副本,让 git 接管(内容一致才可删):

bash
sha1sum backend/public/media/console-panel/jquery.min.js
git show origin/main:backend/public/media/console-panel/jquery.min.js | sha1sum   # 两行一致才继续
rm -rf backend/public/media/console-panel && git pull --ff-only origin main

3.5.4 不要用 git checkout origin/main -- <file> 精确同步 ​

那条命令只改工作区文件内容、不推进 HEAD。用它"热修"几个文件后,生产 HEAD 会长期停在 很早的提交;等后来要做一次完整 pull 时,会一次性把中间所有他人的提交(虚拟主机面板、 换绑手机、官网结算栏等)全量带进生产,部署范围失控。

对策:要么全量 git pull --ff-only;要么坚持精确同步,但必须清楚自己欠了多少提交, 并在部署前把「这次会连带上线哪些别人的提交」讲清楚,让人来决定。

3.5.5 构建前先备份 dist ​

bash
TS=$(date +%Y%m%d-%H%M%S)
tar -czf /tmp/backup/www-dist-$TS.tar.gz frontend-user-v3-www/dist

保留位置 /tmp/backup/,回滚时解包覆盖即可。

3.5.6 PHP 改动不需要重启 PHP-FPM ​

本机 opcache 为 validate_timestamps=On、revalidate_freq=2,同步 PHP 文件后最多 2 秒自动生效。 确认方式:php -i | grep -i opcache。只有改了 php.ini、扩展或 bootstrap/cache 才需要重启。

3.5.7 多台服务器时,先确认当前 SSH 连接指向哪台 ​

有多个已连接的资源时,远程命令会打到「当前活动连接」,可能不是生产机。表现为 cd /www/wwwroot/TuraIDC: No such file or directory。每轮操作前先 ssh_list 确认当前连接, 或用 ssh_connect_profile 显式指定。

3.5.8 长任务用 nohup 后台 + 轮询 ​

远程命令有超时上限(默认 30 秒),构建几十秒就会被中断。正确姿势:

bash
nohup bash -c 'cd /www/wwwroot/TuraIDC && CI=true pnpm run build:frontends > /tmp/build.log 2>&1; echo "exit=$?" >> /tmp/build.log' > /dev/null 2>&1 &
sleep 100 && tail -10 /tmp/build.log     # 轮询直到出现 exit=

3.5.9 部署后必须 grep 产物验证 ​

HTTP 200 只说明站点能开,不代表内容正确(3.5.1 就是 200 全绿但地址错)。收尾至少核对三项: 产物含本次改动的新关键字、新改动前的旧关键字为 0、硬编码域名与 backend/.env 真源一致。

3.5.10 带 TTL 的缓存会让「改了没生效」 ​

console/capabilities 等响应缓存 300 秒、settings 分组缓存 600 秒,管理员改完设置不会立刻生效, 旧值也不会因为重新部署而消失。验证前先手动清对应缓存键。

4. 本地启动差异 ​

本地同样使用直连 API:三个前端分别在 127.0.0.1:5175/5173/5174,API 在 127.0.0.1:8000。前端开发环境的 VITE_API_BASE_URL 已指向 http://127.0.0.1:8000/api,因此会真实验证 CORS 和 VNC WebSocket。

后端用一条封装命令同时拉起 HTTP、Relay 与队列:

bash
cd backend
php artisan app:serve
# 等价于:HTTP + VNC Relay + 业务队列 Worker(queue:work 消费业务队列组)

# 如果本地想同时拉起调度,Relay 另行独立运行;
# 带调度时不再另起常驻 Worker,队列由每分钟 queue:drain 按队列组后台消费
php artisan app:serve --with-schedule --without-vnc
php artisan vnc:relay

细节见 docs/develop/local-development.md 与 backend/app/Console/Commands/ServeBackendStackCommand.php。

5. 常见运维操作 ​

5.1 查看调度运行情况 ​

  • 宝塔计划任务页可看脚本日志
  • backend/storage/logs/laravel.log
  • 管理端 /api/v2/admin/schedules/overview 与 /api/v2/admin/logs/tasks

5.2 手动触发一次调度 ​

bash
cd backend
php artisan schedule:run

5.3 手动跑一次队列消费 ​

正常不需要。只在排查"队列已堆积但未消费"时按目标队列手动运行:

bash
cd backend
php artisan queue:work --once --queue=provision
php artisan queue:work --once --queue=referral,notification,coupon,default
php artisan queue:work --once --queue=automation

5.4 清理失败队列 ​

bash
cd backend
php artisan queue:retry all     # 重试全部失败
php artisan queue:flush         # 清空失败队列(慎用)

5.5 OPcache / 路由 / 配置缓存刷新 ​

上线后如果路由或配置没生效,依次跑:

bash
cd backend
php artisan route:clear && php artisan config:clear && php artisan cache:clear
php artisan route:cache && php artisan config:cache
# 重启 PHP-FPM(宝塔面板 → PHP → 重启)

5.6 PHP 禁用函数问题 ​

Composer 报 Call to undefined function putenv() 等错误时,优先核对宝塔 PHP 的禁用函数和 CLI / FPM 配置差异;当前仓库未保留单独修复指南。

6. 监控与告警 ​

当前已集成 Sentry SDK(需配置 SENTRY_LARAVEL_DSN 激活),临时监控靠:

  • Laravel 日志(backend/storage/logs/laravel.log)
  • 宝塔监控面板
  • /api/v2/admin/logs/* 管理端日志页
  • 健康检查端点:/api/health(存活)、/api/ready(就绪,检查 DB/Cache/Storage/Scheduler)

/api/ready 会分别报告心跳存活、任务运行状态和队列状态;心跳只处理当前 15 分钟槽位,错过的槽位默认不自动回放,需依赖任务幂等补偿或管理端手动触发。

建议的整改方向:

  • 配置 Sentry DSN 并激活错误追踪
  • 关键任务失败告警(自动续费、支付回调、实名回调)
  • 数据库慢查询日志启用 long_query_time=1

7. 回滚策略 ​

  • 后端:代码回退 + composer install + php artisan migrate:rollback(仅当迁移可逆)
  • 前端:按站点独立回滚,直接恢复旧 dist
  • 数据库:禁止在生产直接 migrate:rollback 不可逆迁移,走备份还原
  • 关键业务(支付、开通、返佣)回退前必须先冻结 schedule:run,避免边回滚边产生新异步动作

8. 文件权限(宝塔部署后) ​

bash
sudo chown -R www:www storage bootstrap/cache
sudo chmod -R 775 storage bootstrap/cache
sudo chmod -R 775 public/uploads public/media
# .env 文件必须严格权限,防止凭据泄露
sudo chmod 600 .env

9. 数据库备份 ​

项目提供 MySQL 定时备份脚本 backend/scripts/backup_mysql.sh,从 .env 读取数据库连接信息。

9.1 宝塔计划任务配置 ​

在宝塔面板 → 计划任务 → 添加:

  • 任务类型:Shell 脚本
  • 执行周期:每天凌晨 3:00
  • 脚本内容:
bash
cd /你的项目/backend
bash scripts/backup_mysql.sh

9.2 备份配置 ​

环境变量默认值说明
BACKUP_DIRbackend/storage/backups/mysql备份文件存放目录
BACKUP_RETENTION_DAYS14备份保留天数

9.3 手动备份 ​

bash
cd backend
bash scripts/backup_mysql.sh

9.4 恢复备份 ​

bash
gunzip -c /path/to/backup.sql.gz | mysql -u用户名 -p密码 数据库名

10. OPcache 生产配置 ​

Laravel 生产环境强烈建议启用 OPcache 并优化配置。在宝塔面板 → PHP → 配置文件中添加或修改:

ini
[opcache]
opcache.enable=1
opcache.enable_cli=1
; 根据项目大小调整,建议 256MB
opcache.memory_consumption=256
; Laravel 项目约 8000+ 文件,建议 20000
opcache.max_accelerated_files=20000
; 生产环境关闭时间戳验证,部署后需手动重启 PHP-FPM 或清除 OPcache
opcache.validate_timestamps=0
; 禁用文件保存,减少 I/O
opcache.save_comments=1
opcache.fast_shutdown=1
; 仅缓存项目文件,排除 vendor 中不常用的文件
opcache.blacklist_filename=/www/server/php/82/etc/opcache_blacklist.txt

部署后必须重启 PHP-FPM 使 OPcache 和路由/配置缓存生效:

bash
# 宝塔面板 → PHP → 重启
# 或命令行
sudo systemctl restart php-fpm-82

OPcache 黑名单文件(可选,排除不需要缓存的文件):

text
# /www/server/php/82/etc/opcache_blacklist.txt
/vendor/phpunit/
/vendor/mockery/
/vendor/phpspec/
/vendor/phpstan/

关联文档 ​

基于 AGPL-3.0-or-later 发布