明珠果苑网站运维手册
明珠果苑 · 网站文档
运维手册
重庆明珠果苑农业开发有限公司
一、适用范围与约定
1.1关于手册
本手册覆盖内容管理系统与数字平台云主机的应用部署、数据库、站点、调度与备份恢复,是日常运维与故障处理的操作依据。文中命令与界面路径均按服务器当前实际状态编写,可直接照做。
1.2关于敏感信息
本手册不写入任何账号密码、密钥、令牌。凡涉及凭据之处,一律指向实际存放位置:数据库口令在容器环境变量中(docker inspect 可查),应用配置在 /www/data/ruoyi/ruoyi-office/yudao-server/src/main/resources/application-local.yaml,宝塔面板与 SSH 凭据单独保管,不在本手册内。
二、日常巡检
2.1资源水位
| 项目 | 数值 | 说明 |
|---|---|---|
| 内存总量 / 已用 | 7.7GB / 约 3.9GB(约 47%) | 日常稳定在 45%~50%,余量充足 |
| 交换分区 | 1GB,使用 0 | 尚未触及,说明内存有余量 |
| 系统盘 | 38GB,已用约 19GB,可用约 18GB | 系统盘 38GB,长期保持 18GB 以上可用 |
| /www/data 占用 | 约 1.4GB | 前端、后端、备份与容器数据卷都在此目录下 |
| 容器 | ruoyi-postgres、ruoyi-redis、tdengine | 均在运行 |
2.2重启后检查清单
docker ps 确认三个容器在跑。
ss -tlnp | grep 48080 后端监听(启动约 108 秒,耐心等两分钟)。
ss -tlnp | grep -E '9999|9090' 执行器与调度中心。
curl -s http://127.0.0.1:48080/ -o /dev/null -w '%{http_code}' 返回 401 即正常。
浏览器登录 sys 站,抽查工作台与菜单。
打开内容管理系统首页,确认页面与图片正常。
三、备份与恢复
3.1三层备份体系与现状
备份分三层,一层只管一段,互相不能替代:一层管整台机器,一层管日常误操作,一层管这台机器本身出事。三层缺哪一层,那一段就是完全裸露的。
| 层级 | 手段与存放位置 | 能防什么 | 防不了什么 |
|---|---|---|---|
| 第一层云主机镜像 | 控制台「镜像 / 快照」,存于云平台侧,不占本机目录 | 整机报废、系统盘损坏、系统文件被误删、被入侵后整机退档 | 只能整机回滚,不能单独取回某个文件;退档点之后的数据全丢 |
| 第二层宝塔界面备份 | 宝塔计划任务产物:站点与面板配置在 /www/backup,数据库在 /www/data/backup/db(见 3.2、3.3) | 站点文件被误删改、单个库损坏、配置改错想回退 | 与站点同机同盘,主机丢失、磁盘损坏、欠费清空时一并消失 |
| 第三层本地备份 | H:\系统备份\新网云主机备份\YYYYMMDD\,一个日期一套全量(见 3.6 至 3.9) | 主机宕机、数据被删除或篡改、机房侧故障;可离线留存、可另存异地 | 靠人工执行,不是实时;距最后一次备份之间的数据会丢 |
三层的分界记一句话就够——镜像管「整机没了」,宝塔管「手滑改坏了」,本地管「这台机器本身出事了」。日常小问题多半靠第二层顶住,主机真出事只能靠第一层和第三层。
频率:镜像在大版本上线前打一次、平时每月一次;宝塔是每日自动,不占人工;本地在每次发版时打一次,月末再补一次全量。
每层都要验过才算数:镜像能在控制台以它启动一台临时机器;宝塔看任务日志有产物、体积正常;本地包 gzip -t 通过,再抽一两个文件与线上比对。只打不验,出事那天才发现是空的。
三层之中,第二层已在运行,各项任务、位置与状态见下表;第一层是否已开启自动快照,需登录云主机控制台确认,确认之前按「没有」对待;第三层的做法见 3.6 至 3.9。
| 备份 | 内容 | 位置 | 频率与保留 | 状态 |
|---|---|---|---|---|
| 数据库每日备份 | pgsql_mzgy、xxl_job | /www/data/backup/db/ | 每天 02:30,保留 30 天 | 运行正常,产物可直接恢复 |
| 站点每日备份(宝塔) | 三个站点目录,其中 yudao-server 那份实际就是 440MB 的 jar | /www/backup/site/ | 每天 01:30,保留 3 份 | 运行中,建议把份数改成 1 |
| 面板配置备份(宝塔) | 宝塔自身配置 | /www/backup/panel/ | 每天 | 保留最近 1 至 2 份即可 |
| MySQL 备份(宝塔) | wordpress_sql | /www/backup/database/mysql/ | 每天 01:30,保留 3 份 | 任务显示成功,但份数为 0,待查 |
| 手工全量备份 | 14 类:代码、库、配置、内容管理系统等 | /www/data/backup/mzgy-full-YYYYMMDD-HHMM/ | 按需 | 脚本 mzgy-full-backup.sh 已就绪 |
3.2PostgreSQL 每日自动备份
3.2.1 为什么不用宝塔的「备份数据库」任务
宝塔的数据库备份任务只认它自己安装或登记过的库。本机 PG16 跑在 Docker 容器里,宝塔「数据库 → Pgsql」页显示的是「当前未安装 PgSql 环境」,只给两个按钮:
〔安装 PgSql 环境〕:会在宿主机再装一套原生 PostgreSQL,与容器抢 5432 端口,数据可能进错库。绝对不能点。
〔远程数据库〕:把容器里的库登记成远程库,面板要校验版本与权限,对容器内数据库支持不可靠,可能登记后仍然备份失败。
结论:因此改用宝塔「计划任务 → Shell脚本」:仍然是面板排期、面板看日志,脚本内部直接进容器跑 pg_dump,不依赖面板的数据库模块,也不受面板升级影响。
3.2.2 任务配置
| 字段 | 值 |
|---|---|
| 任务类型 | Shell脚本(不要选“备份数据库”) |
| 任务名称 | 备份PostgreSQL数据库 |
| 执行周期 | 每天 02:30(刻意错开 01:30 的网站与 mysql 备份,避免三条任务抢磁盘) |
| 执行用户 | root |
| 进程锁 | 开启(防止上一轮未结束时重复执行) |
| 日志文件 | /www/server/cron/<任务哈希>.log,宝塔任务列表点「日志」可看 |
3.2.3 脚本机制
用 docker exec ruoyi-postgres pg_dump -U "$POSTGRES_USER" -Fc 导出,用户名取自容器环境变量,脚本里不出现任何密码。
先写临时文件 .part,用 pg_restore -l 读一遍目录,确认能读出对象才改成正式文件名,避免半截文件冒充备份。
小于 10KB 一律判定无效,防止空文件显示成功。
任何一项失败即 exit 1,宝塔任务列表会标红,不会再出现“显示成功但没东西”。
保留最近 30 天,自动清理更早的 dump;每次运行追加写入 /www/data/backup/db/backup.log。
3.2.4 验证方法
ls -lh /www/data/backup/db/
# 确认 dump 里确实带数据段(正常应为 8,xxl_job 共 8 张表有数据)
cat /www/data/backup/db/xxl_job_YYYYMMDD.dump | docker exec -i ruoyi-postgres \
pg_restore -l | grep -c "TABLE DATA"
# 对照库内实际数据量
docker exec ruoyi-postgres psql -U pgsql_mzgy -d xxl_job \
-tc "select relname||'='||n_live_tup from pg_stat_user_tables order by n_live_tup desc limit 6"
踩坑:pg_restore -l 后面不要写 – 当文件名,某些版本会报 could not open input file "-";省略文件名走管道即可。
实测基准:pgsql_mzgy 约 1.3MB、xxl_job 约 22KB。xxl_job 体积小是因为该库总共只有 8 行数据,主要体积是表结构,属正常。
3.2.5 失败排查
| 现象 | 原因 | 处理 |
|---|---|---|
| docker: command not found | cron 环境 PATH 里没有 docker | 脚本已内置 PATH;仍失败则改用 /usr/bin/docker 全路径 |
| permission denied | 执行用户非 root | 把任务执行用户改为 root |
| 产物只有几十字节 | 库连接失败或 dump 中途中断 | 看 /www/data/backup/db/backup.log,并在终端手动跑一次定位 |
| 任务列表显示失败 | 脚本以非 0 退出,通常是校验没过 | 先看日志,再看磁盘是否写满 |
3.3宝塔自带备份的现状与调整建议
站点备份把 /www/data/ruoyi/frontend 与 yudao-server 的 jar 目录都当成了“站点”在备,其中 yudao-server 那份每份 440MB,保留 3 份就是 1.3GB。jar 本地随时能重新编译,保留 1 份应急即可,建议把该任务的保留份数由 3 改成 1。
MySQL 那条任务每天显示成功,但备份份数为 0,需要进任务日志确认是否真的产出了文件。内容管理系统数据虽然变动少,但没有备份等于裸奔。
宝塔自带的 /www/backup 与业务备份目录 /www/data/backup 是两回事,清理时不要混,也不要把业务备份放错地方。
3.4恢复演练
备份能读出来不等于能恢复进去。建议找时间做一次演练,全程只碰临时库,生产库只读:
docker exec ruoyi-postgres psql -U pgsql_mzgy -c "create database pgsql_mzgy_verify"
cat /www/data/backup/db/pgsql_mzgy_YYYYMMDD.dump | docker exec -i ruoyi-postgres \
pg_restore -U pgsql_mzgy -d pgsql_mzgy_verify --no-owner 2>&1 | tail -15
docker exec ruoyi-postgres psql -U pgsql_mzgy -d pgsql_mzgy_verify \
-tc "select count(*) from information_schema.tables where table_schema='public'"
# 表数对上生产库(327 上下)后清理 docker exec ruoyi-postgres psql -U pgsql_mzgy -c "drop database pgsql_mzgy_verify"
恢复演练只在临时库上进行,生产库全程只读。做完一次,“备份可用”才算从推测变成事实。
3.5备份目录规范
上传文件、备份产物一律放 /www/data/backup/,不散落在业务目录里。
命名带日期:数据库 dump 用 库名_YYYYMMDD.dump;jar 用 yudao-server-YYYYMMDD-HHMM.jar;全量备份用目录 mzgy-full-YYYYMMDD-HHMM。
业务目录 /www/data/ruoyi 下不要长期堆放前端历史版本目录,需要留档就压缩后移入 backup,否则过几个月分不清哪个是线上。
3.6本地归档:一个日期一套,两个系统
服务器上的每日备份只能防误删,防不了主机失联——备份和站点在同一块盘上。因此在服务器之外再建一套本地归档:每次发版或月度盘点,把两个系统各取一份完整包,按日期存放,出事时整套拿走就能还原。
目录结构固定为「日期 → 两个系统 → 固定子目录」,一个日期目录就是一次全量,里面不再按版本细分。每个位置放什么、从哪里取,见下表。
| 目录 | 放什么 | 从哪取 |
|---|---|---|
| WordPress\整站\ | 整站打包,含 wp-config.php 与 wp-content\uploads | /www/wwwroot/明珠果苑.com/ |
| WordPress\数据库\ | mysqldump 导出的 wp 库 | 宝塔 MySQL,库名 wordpress_sql |
| Yudao\后端源码\ | 本地工程整体打包,含 .git,排除 target 与 .idea | 本地 H:\Ruoyi Office\ruoyi-office |
| Yudao\后端编译\ | yudao-server.jar 与 xxl-job-admin-2.4.0.jar | /www/data/ruoyi/backend/、/www/data/xxl-job/ |
| Yudao\前端源码\ | 本地工程整体打包,含 .git,排除 node_modules 与 dist | 本地 H:\Ruoyi Office\ruoyi-office-vben |
| Yudao\前端编译\ | dist 打包,含手改过的 index.html | /www/data/ruoyi/frontend/ |
| Yudao\数据库\ | 全量 dump 加建表脚本,含 xxl-job 建表 | pg_dump -Fc pgsql_mzgy、xxl_job |
| Yudao\配置文件\系统\ | systemd 单元、crontab、nginx 全量、SSL 证书、xxl-job | /etc/systemd/system 等 |
| Yudao\配置文件\系统\security\ | iptables 规则、sshd 配置、ufw 状态、加固项 | iptables-save、/etc/ssh、/etc/sysctl.d |
| Yudao\上传文件\ | 附件目录 | 位置待确认 |
四条规矩。一是源码那两格只能从本地工程打包,服务器上没有源码,取不到;二是统一用 tar.gz,不用 zip,zip 在 Windows 下按 cp936 记文件名,拿到 Linux 解包中文目录会变乱码;三是应用配置(application*.yaml、logback-spring.xml)全部打包在 yudao-server.jar 内,服务器上没有外部配置文件,备份了 jar 就等于备份了配置,不要单独拆出来;四是每个系统都要有一份 MANIFEST.txt,记文件名、大小、MD5、来源路径和构建信息,没有它,半年后没人分得清哪个 jar 对应哪次提交。
旧目录 20261003 是 10 月 3 日发版前的快照,按旧命名存放,不迁移、不再往里加内容,只作回退点保留。
本地误删的编译产物先别急着重编译,去回收站找回来再说——这次发版用的那份 18:11 产物,就是从 H 盘回收站里捞回来的。产物归档一律带时间戳,例如 web-antd-dist_20261025_181101.zip,与 dist.zip 共存,避免新旧两个同名包互相覆盖;也不要为了图省事把归档改回 dist.zip。
3.7一次备份怎么做
顺序不要调:前五步在服务器上取数,第六步在本地,最后两步做核对与归档。
| 步骤 | 动作 | 命令或要点 |
|---|---|---|
| 1 | 后端产物 | 把 /www/data/ruoyi/backend/yudao-server.jar 拷出,文件名带日期与构建时间 |
| 2 | 前端产物 | tar czf frontend-YYYYMMDD.tar.gz -C /www/data/ruoyi/frontend . |
| 3 | 平台数据库 | docker exec ruoyi-postgres pg_dump -U pgsql_mzgy -Fc pgsql_mzgy > pgsql_mzgy_YYYYMMDD.dump |
| 4 | 系统与配置 | 打包 /etc/systemd/system 服务单元、crontab -l、nginx 站点与 extension、证书目录、/www/data/xxl-job 整个目录(含调度 jar)、iptables-save 结果;应用配置在 jar 内不用单独取 |
| 5 | 内容管理系统 | tar czf site-YYYYMMDD.tar.gz -C /www/wwwroot/明珠果苑.com . 再用 mysqldump 导出 wp 库 |
| 6 | 源码 | 本地打两个工程包,排除 node_modules、target、.idea,保留 .git |
| 7 | 算校验 | 逐文件算 MD5,填 MANIFEST.txt,附 commit、构建时间、JDK 与 pnpm 版本 |
| 8 | 归档 | 放进 H:\系统备份\新网云主机备份\YYYYMMDD\,按 3.6 的目录落位 |
取完先别收工,确认两件事:jar 和 dist 是不是这次真要上的那一版(对 commit 与构建时间);dump 是不是完整的(pg_restore -l 能读出对象目录,体积也不是几 KB)。
3.8恢复怎么做
顺序不能颠倒:先环境、再配置、再产物、最后数据。产物先上而配置后到,服务一定起不来,日志里报的是找不到配置或数据源,很容易误判成代码问题。
| 顺序 | 做什么 | 落点或命令 |
|---|---|---|
| 1 | 装环境 | 按 配置文件\系统 里的版本清单对齐 PG 16、MySQL、JDK 17、nginx、Redis |
| 2 | 放配置 | 服务单元、nginx、证书、crontab 按 配置文件\系统\还原路径对照.txt 逐项拷回,再 systemctl daemon-reload;防火墙执行 iptables-restore < security\iptables.rules 还原 14 条封堵规则;应用配置在 jar 内,无需单独放 |
| 3 | 放后端 | yudao-server.jar 放 /www/data/ruoyi/backend/,宝塔 Java 项目指向它;xxl-job-admin-2.4.0.jar 放 /www/data/xxl-job/,必须是 PG 改造版,不要换成官方原版 |
| 4 | 放前端 | 清空 /www/data/ruoyi/frontend/ 后再解 dist,不要覆盖式解压 |
| 5 | 导数据库 | 先建空库,再 pg_restore –no-owner;菜单缺项补跑 数据库\部署脚本\ |
| 6 | 放附件 | 上传文件回原目录,路径与备份时保持一致 |
| 7 | 起服务 | 后端走宝塔「Java 项目」启动,不要 systemctl start yudao-server(该系统单元已 disable,两套同开会抢 48080);再 systemctl enable –now xxl-job-admin,看日志无异常 |
| 8 | 内容管理系统 | 整站解到 /www/wwwroot/明珠果苑.com/,导 wp 库,改 wp-config.php 的库名与域名 |
验收六条:后端进程在且日志无启动异常;curl 请求 /admin-api 正常返回;前端首页打开不白屏;能登录;左侧菜单树不缺项;内容管理系统首页与后台都能进。缺一条就是没恢复完。
第 4 步必须「先清空再解压」。前端目录里残留旧 hash 文件时,页面会去请求已删除的 chunk,表现是白屏或一直转圈,看着像代码坏了,其实只是没清干净。
3.9保留与异地
只保留最近 3 次全量,加每月末 1 次长期归档,其余删掉。一次约 700MB(其中后端与调度两个 jar 占 590MB),全留一年就是几十 GB,翻找的时间成本比硬盘贵。
H 盘和源机同处一地,至少再放一份到移动硬盘或对象存储。备份与主机同时消失的场景并不罕见,不异地的备份等于没有备份。
四、安全加固
4.1加固内容
| 层面 | 措施 |
|---|---|
| 端口收敛 | 后端 48080、PostgreSQL 5432、Redis 6379、MySQL 3306、调度 9090 等敏感服务全部绑 127.0.0.1;公网只露 80(网站)和宝塔面板端口(安全入口加 BasicAuth) |
| 系统层 | 开宝塔防火墙、禁 ping;SSH 登录宽限和空闲超时、密码策略、移除空密码登录、限制 su、去掉 sudo 免密 |
| 内核与应用 | 开 SYN Cookie 防 SYN 洪水;PHP 关掉错误显示;调度中心用强随机令牌 |
4.2加固验收
加固完扫了一遍:0 紧急、0 高风险。所有改动都留了 .bak,能逐项回滚。
五、权限与租户维护
完整方案见《明珠果苑权限模型落地方案》。核心原则一句话:上下级关系用部门树加数据权限,平级隔离用租户。租户管隔离,层级归业务树。
5.1六分类租户
| 编号 | 租户名 | 分类 |
|---|---|---|
| 1 | 明珠果苑 | 系统(平台方,超管可跨租户查看) |
| 121 | 伙伴1 | 伙伴 |
| 122 | 基地1 | 基地 |
| 123 | 渠道1 | 渠道 |
| 124 | 单位1 | 单位 |
| 125 | 备用1 | 备用 |
租户编号(tenant_id)不能改,全库几十张表引用它,改动等于级联灾难。
渠道体系(一、二、三级)用系统租户内的部门树加数据权限「本部门及以下」实现,线上、线下用部门命名标识(如「线上·××」「线下·××」)。
基地、伙伴各建一个租户;游客与 C 端用会员体系,不建租户、不进后台。
5.2角色体系
| 顺序 | 角色 | code(不可改) | 类型 |
|---|---|---|---|
| 1 | 超级管理员 | super_admin | 内置 |
| 2 | 技术总监 | common | 内置 |
| 3 | 人事总监 | crm_admin | 内置 |
| 4 | 市场总监 | test-dp | 自定义 |
| 5 | 访客 | guest_readonly | 自定义 |
内置角色界面拒绝编辑,需直接改库;只改 name 与 sort,code 一律保留(框架识别用)。
超管账号 yanghsia(昵称杨夏,id=1),日常管理用此号。
5.3跨租户机制
链路为:前端租户切换组件携带 visit-tenant-id 请求头 → 后端 TenantVisitContextInterceptor 校验 → 放行超管跨租户查询。菜单 5010(system:tenant:visit)是入口。
注意:layouts/basic.vue 中的租户切换组件必须保持渲染,不得加 v-if 之类的限制,否则顶栏「请选择租户」下拉会消失。
5.4菜单授权规则与一次越权复盘
先分清:目录(type=1)是子菜单的容器,菜单(type=2)是页面,按钮(type=3)是页面里的操作。三者串起来才是一条可见的授权路径。
规则一:目录节点必须随子菜单一起授予。只给子菜单、不给它上面的目录,前端拿不到父节点,子菜单根本不显示,看起来像授权没生效。要整棵排除用递归条件,例如 m.id <> 1224 即排除「租户管理」及其全部子节点。
规则二:按最小必要给。角色只挂该岗位真用得上的目录与菜单;按钮只给查询类权限码(query、get、list、monitor、preview),增、删、改、导入、导出一律不给;数据权限上,本部门岗位给「本部门及以下」,别图省事给全部。
复盘:渠道账号(system_users id=143,tenant_id=123)曾绑到租户管理员角色 156,该角色挂了 257 个菜单,等于平台全量,渠道登录后因此看得到明珠果苑租户的信息。处置:把角色 156 的「1224 租户管理」及其子树(1138、1139–1143、5010)取消授权,渠道就看不到了。
复查:新增渠道、伙伴账号后,先看它绑的角色有多少条菜单授权,接近全量的立刻警觉。按角色统计条数,第一名最可疑:SELECT role_id, COUNT(*) FROM system_role_menu GROUP BY role_id ORDER BY 2 DESC;
六、内容管理系统运维与安全
6.1安全观察与建议
内容管理系统访问日志每天增长约 20MB,几乎全部是扫描:ThinkPHP、Magento、Symfony 漏洞探测,后门文件探测,AI Agent 协议扫描(/.well-known/*),以及 referrer 为垃圾站点的请求。日志里返回的都是 Primary script unknown 或 not found,说明这些攻击没有成功,我们用的是 PHP + WordPress,与它们打的技术栈不匹配。
建议在宝塔「网站防火墙」里开启 WAF,让扫描在 nginx 之前就被挡掉,既省 CPU 也省日志。不要手写一堆 nginx 拦截规则,升级或换机容易丢。
四个域名打开的是同一份内容。主域为 www.mzgarden.com,www.明珠果苑.com、www.明珠果苑.中国、www.明珠果苑.网址 建议 301 到主域,避免搜索引擎视为重复站点。
注意:主题 functions.php 中同名函数只能定义一次,重复声明会触发 Cannot redeclare 致命错误,改主题后要用无痕窗口验证。现行代码只有一处定义。
6.2内容管理系统改版与素材规范
背景音乐:浏览器一律禁止自动播放,只能由用户点击开启。页面上要留一个明确的开启入口,不要指望进场自动响,否则音乐永远放不出来。
Hero 图:上线前必须压缩,WebP 最佳,单张不超过 300KB。超了首页首屏会明显拖慢,手机端尤其吃亏。
动画性能:水滴类装饰动画控制在 14 个以内,手机端按屏宽缩小尺寸;系统里开了「减少动画」的用户要整组关掉动画(prefers-reduced-motion),别让装饰拖慢首屏。
顶部导航不再靠「外观 → 菜单」手工维护:主题按分类目录树自动生成菜单,后台新建分类、前台自动多一项,排序按分类建立顺序,未分类不显示。分类简介写在分类描述里,会显示在前台分类页。要调整顺序就调分类的顺序或层级,不要回外观里再搭一套静态菜单,两套同时存在会互相打架。
6.3维护模式日常运维
维护模式由自制插件「明珠果苑维护模式 mzgy-maintenance」提供,用于整站升级、改版期间给未登录访客显示一个友好的临时页面,而不是报错或白屏。开关在后台「设置 → 明珠果苑维护」,一个勾选框,状态存于选项 mzgy_maintenance_active。
勾上后,未登录访客访问任意前台页面统一返回 503(带 Retry-After: 3600 头,提示搜索引擎稍后再来,不会误判为宕机)并显示维护页;已登录管理员照常浏览前台。后台、登录页、REST 接口始终放行,所以勾了维护也能正常登录、进后台。管理员顶栏会出现红色「维护模式:已开启」标记,防止忘关。
维护页有两个候选模板,靠重命名切换、不是复制覆盖。插件目录 /www/wwwroot/明珠果苑.com/wp-content/plugins/mzgy-maintenance/ 下:
| 候选文件 | 适用场景 | 页面内容 |
|---|---|---|
| 建设中maintenance.php | 网站上线前 | 「网站建设中」,带上线倒计时与数字管理平台入口 |
| 维护中maintenance.php | 升级、维护时 | 「网站升级维护中」,简洁提示页 |
插件实际加载的是同目录的 maintenance.php。把哪个候选文件名前的中文前缀去掉、重命名为 maintenance.php,前台就显示哪个;两个候选文件都长期保留在目录里,谁叫 maintenance.php 谁生效。同目录只能有一个 maintenance.php,是重命名切换,不要复制覆盖。
日常操作按下面的顺序:
| 场景 | 动作 | 要点 |
|---|---|---|
| 开维护 | 先确认 maintenance.php 是要展示的页,再到后台勾选「网站维护中」并保存 | 页面没重命名到位时,先按下面“切页”处理,再勾开关 |
| 关维护 | 后台取消勾选即恢复 | 页面文件不用动,留着下次再用 |
| 改文案 | 直接编辑对应候选文件(维护中maintenance.php / 建设中maintenance.php) | 它当前就是 maintenance.php 时保存即生效;否则先重命名切换;配色按莫兰迪绿 #5B7B5E 已设定 |
| 切页(建设中/维护中互换) | 在插件目录重命名:先把现有的 maintenance.php 改回带中文前缀的名字,再把要用的候选重命名为 maintenance.php | 是重命名切换,不是覆盖;任何时候同目录只有一个 maintenance.php |
开维护后自检四条:无痕窗口(未登录)打开前台,应看到维护页且 HTTP 状态为 503;登录管理员后打开前台,应正常看到网站而不是维护页;后台、登录页、接口可正常访问;维护结束后顶栏红标消失、无痕窗口恢复正常。
先分清 503 和 403,别误判。整站对未登录访客返维护页、状态码 503,是维护模式在生效;只有单篇文章对游客返 403、登录后能看,是内容卫士 mzgy-guard 的阅读权限控制(见开发手册 4.4),不是维护模式。核对维护中的内容时,用接口取 content.raw 或登录管理员看,不要用游客前台判断。
维护页是纯 HTML、无外部依赖,单独显示不会因样式或资源加载失败而变形。改了主文件 mzgy-maintenance.php 的逻辑后,必要时在后台「插件」页停用再启用一次。不要让静态 index.html 维护页与本插件两套机制并存(见开发手册 4.5)。
七、故障排查
7.1常见故障速查
| 现象 | 先查什么 | 处理 |
|---|---|---|
| 平台打不开或 502 | ss -tlnp | grep 48080;docker ps | 后端没起来就重启 Java 项目;容器停了就 docker start |
| 登录后菜单空白 | 浏览器 F12 看接口;Redis 缓存 | 清 Redis 缓存后 Ctrl+F5 |
| 改了 jar 重启不生效 | pgrep -af 'yudao-server\.jar' 是否只一个 | 确认不是 systemd 那套在跑;必要时 disable 它 |
| 内存突然变高 | ps aux –sort=-%mem | head -12 | 先认进程再动手,别 kill 服务进程 |
| 磁盘增长快 | du -sh /www/* | sort -rh | 先看是日志、备份还是历史目录 |
| 内容管理系统白屏 | nginx 错误日志、PHP-FPM 是否在跑 | 看 error.log 定位,不要急着重装 |
| 定时备份没执行 | 宝塔计划任务列表的“上次执行” | 点「执行」手动跑一次,看日志定位 |
| 点菜单显示“功能建设中·敬请期待” | 该菜单挂的是统一占位页 common/coming-soon/index | 属预期,页面还没开发;开发完成后把该菜单的 component 改指真实页面。平台现有 62 个菜单是这么挂的 |
| 访客整站看到维护页、返回 503(非预期) | 后台「设置 → 明珠果苑维护」的勾选状态 | 没在维护就取消勾选;正在维护属正常;放行规则见 6.3 |
| 勾了维护却不显示维护页 | 插件目录里 maintenance.php 是否存在、是否重名 | 把要用的候选重命名为 maintenance.php,同目录只能有一个,是重命名切换不是覆盖 |
| 单篇文章游客 403、登录后能看 | 内容卫士 mzgy-guard 的阅读权限 | 属正常访问控制,不是维护模式;整站 503 才是维护,见 6.3 |
7.2后端类
| 现象 | 原因 | 解决 |
|---|---|---|
| 内存被白占 1.2GB | systemd 与宝塔各起一套同一份 jar | systemctl disable + stop systemd 那套 |
| 改了 jar 重启不生效 | systemctl restart 重启的不是在服务的那套 | 统一走宝塔 Java 项目页重启 |
| jar 启动报类缺失 | 依赖未声明 + 实现类丢失 + 嵌套 jar 扫描 | 见 4.6 |
| 启动卡死在 iotScheduler | iot 模块 Quartz 调度器阻塞 | iot 模块暂停启用 |
| 产物 3.4GB | 未带 -P boot,走了 cloud profile | 固定命令带 -P boot |
| 打包报 IotDeviceStateEnum 找不到 | -DskipTests 仍编译测试代码,测试类 import 路径与主代码不符 | 改用 -Dmaven.test.skip=true |
| 运行中 mvn clean 导致 jar 消失 | clean 删除 target | 运行期间不要 clean,打包用 package |
| PowerShell 下打包参数失效 | -D 参数被拆 | 参数加双引号 |
| 定时任务页面 404 | 后端 JobController 被裁剪 | 方案 A 移植(待实施) |
7.3前端类
| 现象 | 原因 | 解决 |
|---|---|---|
| 白屏、主 JS 404 | jse 目录被改名 js,index.html 引用 /jse/ | 重新解压 dist.zip 恢复目录结构 |
| 入口内部资源 404 | 硬编码 /web/ 前缀未配 Nginx | 加 location ^~ /web/ rewrite |
| 登录后“系统异常” | 源码比 SQL 新,system_dept 缺 org_type 列 | ALTER TABLE 补列 |
| 登录后 404 | defaultHomePath 指向 /workspace 但菜单表无记录 | 补工作台菜单 |
| 菜单重复导致路由崩 | SQL 跑了两遍 | 删除重插,只执行一次 |
| 子菜单 404 | 子菜单 path 带斜杠,拼出双斜杠 | path 不带斜杠 |
| “文档地址”提示条 | VITE_APP_DOCALERT_ENABLE 未关 | .env.production 置 false 后重编译 |
| 改偏好默认值不生效 | localStorage 缓存旧偏好 | 偏好面板点一次或清缓存 |
| 构建崩在 jiti | vite 与 jiti 版本冲突 | 保留 resolve.alias 的浏览器 mock |
| 已登录,发版后点菜单白屏 | 发版后懒加载 chunk 已换名,旧页面请求旧 chunk 404 | 前端已内置 vite:preloadError 自愈,自动刷新一次即恢复;仍反复见部署手册 4.8 |
| 常规窗口总是旧版,无痕正常 | 浏览器会话恢复,关闭重开是整页快照、零请求 | 按本节后面三条 Edge 设置处理;普通用户无需处理 |
先分诊:无痕窗口打开正常、地址栏手输完整网址回车也正常,说明服务器上只有一个版本,是浏览器本地把旧页面留住了;若两种方式都仍是旧版,先回上面的表查服务器,不要动浏览器。
为了能实时观看修改后的效果,避免页面缓存造成旧版样式驻留,Edge 需按下面三条调整。
| 条目 | 作用 | 性质 |
|---|---|---|
| 第 1 条:启动时打开新标签页 | 直接关掉“恢复上一个会话标签页”这个动作——旧页面快照不再被还原 | 决定性,治本 |
| 第 2 条:关启动加速 + 关后台继续运行 | Edge 默认“窗口关了、进程还常驻”。你以为是重新启动,其实是唤起那个带着旧会话的常驻进程,第 1 条的“启动时”逻辑可能根本没走。关掉后 Edge 才真正退出 | 保障,让第 1 条稳定生效 |
| 第 3 条:结束 msedge.exe 再打开 | 不是设置,是一次性清场:把当时常驻、持有旧会话的进程杀掉,让新设置从干净进程启动 | 只第一次需要 |
三条对应的开关位置:设置 → 起始、主页和新建标签页 → “启动时”选“打开新标签页”;设置 → 系统和性能 → 关掉“启动增强”与“关闭 Microsoft Edge 后继续运行后台扩展和应用”。
普通用户无需处理:输入网址、点链接、从收藏夹进入都属于正常导航,no-cache 会自动取到最新版;即便极端情况下恢复了登录页快照,也只看一次,登录跳转或刷新即自动更新。
7.4数据库类
| 现象 | 原因 | 解决 |
|---|---|---|
| 长期没有数据库备份 | 宝塔原生任务不认容器内的 PG,一直没建对应任务 | 建 Shell脚本 任务,失败会标红 |
| 备份“显示成功”却没文件 | 宝塔 MySQL 备份任务只刷状态 | 查任务日志,必要时同样改脚本方式 |
| SQL 插入整批失败 | system_role_menu.id 无自增 | 显式 id,MAX(id)+ROW_NUMBER() |
| 导错库 | 宝塔数据库页不显示 Docker 容器 | 以 docker ps 为准 |
| json 字段类型不符 | 官方 PG 版惯例用 text | jsonb 改 text |
| 中文域名加不进宝塔 | 面板输入框拦截 | 命令行 sed 写 punycode |
7.5站点与磁盘类
| 现象 | 原因 | 解决 |
|---|---|---|
| 磁盘被占 1.3GB | 站点备份把 jar 目录当站点,每天备一份 440MB,留 3 份 | 保留份数改 1;jar 需要时手动存 backup |
| 删了文件磁盘没变 | 宝塔删除先进回收站 | 清空回收站后空间才真正释放 |
| “内部服务器错误” | proxy_pass 尾部斜杠吃掉 /admin-api | 去掉尾部斜杠 |
| curl 404 但浏览器正常 | Host 头不匹配,走了默认站点 | curl 加 -H "Host: …" |
| conflicting server name | 同一域名配在两个站点 | 删除重复站、去重 server_name |
| sed 改配置后域名重复 | 同一条 sed 执行了两遍 | 改前先备份,改后用 grep 核对 |
| PageAdmin 目录已删但进程还在 | dotnet 进程加载着 dll,不随目录消失 | kill 该进程,确认端口释放 |
附录 APostgreSQL 每日备份脚本全文
脚本存档在 H:\系统备忘\常用\备份命令\mzgy-pg-backup-daily.sh,服务器上的执行副本存放在宝塔计划任务「备份PostgreSQL数据库」的脚本框内。迁移或重建时,把下面全文粘贴到「计划任务 → 添加任务 → Shell脚本」即可,无需修改任何变量。
#!/bin/bash # ============================================================ # 明珠果苑网站 — PostgreSQL 每日备份 # 容器 ruoyi-postgres (postgres:16),库:pgsql_mzgy、xxl_job # 用途:粘贴到宝塔「计划任务 → Shell脚本」,每天 02:30 执行 # 2026-10-23 建立 # ============================================================
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
CONTAINER="ruoyi-postgres" OUT="/www/data/backup/db" DBS="pgsql_mzgy xxl_job" KEEP_DAYS=30 TODAY=$(date +%Y%m%d) LOG="$OUT/backup.log"
mkdir -p "$OUT"
log() { echo "[$(date '+%F %T')] $*" | tee -a "$LOG"; }
# 取容器内默认超级用户名,不写死、不落密码 PGUSER=$(docker exec "$CONTAINER" printenv POSTGRES_USER 2>/dev/null) if [ -z "$PGUSER" ]; then log "失败:取不到 $CONTAINER 的 POSTGRES_USER,容器可能在重启" exit 1 fi
fail=0
for db in $DBS; do
final="$OUT/${db}_${TODAY}.dump"
part="$final.part"
if ! docker exec "$CONTAINER" pg_dump -U "$PGUSER" -Fc -d "$db" > "$part" 2>>"$LOG"; then
log "失败:$db 导出出错(详见上方 pg_dump 报错)"
rm -f "$part"
fail=1
continue
fi
size=$(stat -c %s "$part" 2>/dev/null || echo 0)
# 校验质量:文件不能太小,且目录(TOC)必须能被 pg_restore 读出
if [ "$size" -lt 10240 ]; then
log "失败:$db 备份只有 ${size} 字节,判定为无效"
rm -f "$part"
fail=1
continue
fi
if ! docker exec -i "$CONTAINER" pg_restore -l < "$part" > /dev/null 2>>"$LOG"; then
log "失败:$db 备份文件损坏,pg_restore 无法读取目录"
rm -f "$part"
fail=1
continue
fi
mv -f "$part" "$final" chmod 600 "$final" log "成功:$db $(du -h "$final" | cut -f1) $final" done
# 清理超过保留期的备份 find "$OUT" -maxdepth 1 -type f -name '*.dump' -mtime +"$KEEP_DAYS" | while read -r old; do rm -f "$old" log "清理:$old" done
if [ "$fail" -ne 0 ]; then log "本次备份有失败项,请检查" exit 1 fi
log "两个库全部备份成功,当前目录占用 $(du -sh "$OUT" | cut -f1)" exit 0
脚本里有两处容易被误改:KEEP_DAYS 是保留天数(改小会提前删历史备份);DBS 是库名列表,用空格分隔,增减库只需改这一行。
全文完