1. 用途和边界
用于 DGJ 线上多台应用节点发布销售明细日期范围校验、前端错误提示、静态资源版本等小范围变更。本文同时适用于后续同类 PHP/旧版 JS 文件发布,但每次执行都必须重新生成版本清单和节点前置哈希。
本手册只描述执行方法,不代表已经发布。2026-09-18 的验证轮次只读检查了线上节点,没有创建线上备份、覆盖文件、重启服务或清缓存。
生产执行前必须取得当次发布授权,并确认发布工具、流量摘除/灰度方式和回滚负责人。不要把内网调试机的备份当成生产节点备份。
2. 已验证的线上事实
- JumpServer 资产
Prod-DGJ-1至Prod-DGJ-7分别对应七台 DGJ 线上节点;实际主机名与资产编号一致。 - 线上实际代码根目录是
/data/www/dist,不是内网调试机使用的/data/www/dgj。 - 五个目标文件在七台节点上都存在,SHA-256 完全一致,线上版本为
prod.2.26.70。 - 七台节点的
/usr/local/php/bin/php -l application/controllers/Report.php均通过。 - 七台节点 OPcache 均为
opcache.validate_timestamps=On、opcache.revalidate_freq=2。通常不需要因为这次文件发布重启 PHP;发布后等待并用实际请求确认即可。
本轮没有把生产节点 IP 写入知识库正文。准确的 IP、资产 ID 和权限以 JumpServer 当前资产列表为准,避免节点清单漂移。
3. 本次待发布清单
代码来源:DGJ2 分支 bugfix/20260918-search-6m,HEAD 5169dcec879ef7aaf73843efcca495ea6c0b9537。
| 文件 | 用途 | 本地待发布 SHA-256 |
|---|---|---|
application/config/version.php | 静态资源版本号 prod.2.26.72 | 2451b1d937c30bc83d73fdbbc7ee0e802600b2ee859bdef79cc2018dbcb588d7 |
application/controllers/Report.php | 销售明细接口日期范围校验 | 8f2b1f91cbc6a248b39d51cf53207e03c399c35b80148e51765e6972145c5291 |
application/views_v2/report/sales-detail.php | 使用 assets() 输出带版本号的 JS 地址 | 1ce7f7229c2a008043db1b0cd6f2a488df1927ed0ae4ceaf8f0417f9ff45ff45 |
statics_v2/old/js/dist/salesDetail.js | 总部销售明细页面提示与初始化等待 | e2595a9963275cd84fe62800a5bf64195ddc2962496582677bf4bd36a71ec890 |
statics_v2/old/js/dist/salesDetailStation.js | 服务站销售明细页面提示与初始化等待 | 3a55cc91768383cc11a95ebcd947cb5bad7fb48c9e1a12ba56235524d26ea6f9 |
这五个文件要作为一个版本发布。不能只替换 PHP 或只替换一个 JS,否则可能出现后端已经拒绝请求、浏览器仍执行旧 JS,或页面加载新 JS 但 PHP 模板仍引用旧资源版本的混合状态。
4. 发布前本地检查
在发布机或代码工作区执行,不要从未确认的工作区直接取文件:
cd /path/to/dgj2.0
git symbolic-ref --short HEAD
git status --short
git diff --check origin/develop...HEAD
php -l application/controllers/Report.php
node --check statics_v2/old/js/dist/salesDetail.js
node --check statics_v2/old/js/dist/salesDetailStation.js
sha256sum \
application/config/version.php \
application/controllers/Report.php \
application/views_v2/report/sales-detail.php \
statics_v2/old/js/dist/salesDetail.js \
statics_v2/old/js/dist/salesDetailStation.js
将最后一段输出保存为本次 release.sha256。PHP 语法检查应使用与线上兼容的 PHP 7.4 运行时;不要仅凭编辑器或 JS 检查通过就认为 PHP 可发布。
5. 七台节点的只读前置核验
通过 JumpServer 对每个资产使用精确资产搜索,登录后先确认身份,再执行以下只读命令。不要用模糊搜索直接选第一条结果。
hostname
hostname -I
test -d /data/www/dist
sha256sum \
/data/www/dist/application/config/version.php \
/data/www/dist/application/controllers/Report.php \
/data/www/dist/application/views_v2/report/sales-detail.php \
/data/www/dist/statics_v2/old/js/dist/salesDetail.js \
/data/www/dist/statics_v2/old/js/dist/salesDetailStation.js
grep -n 'sys_version' /data/www/dist/application/config/version.php
/usr/local/php/bin/php -l /data/www/dist/application/controllers/Report.php
/usr/local/php/bin/php -i | grep opcache.validate_timestamps
/usr/local/php/bin/php -i | grep opcache.revalidate_freq
前置门禁:主机名/IP 与 JumpServer 资产一致、五个文件均存在、每个前置哈希都已记录、目标目录确实是 /data/www/dist。任何一项不一致都停止,不要猜目录或覆盖。
6. 推荐发布流程
6.1 生成发布批次和每节点备份
每个节点必须有独立备份目录,不能七台共用一个备份目录。建议命名为:
/data/www/.backup-sales-detail-<RELEASE_ID>/<HOSTNAME>/
<RELEASE_ID> 使用时间戳或发布单号;<HOSTNAME> 由远端 hostname 实际输出得到。得到发布授权后,在每台节点上按以下顺序执行:
RELEASE_ID=<release-id>
HOSTNAME_REAL="$(hostname)"
BACKUP_ROOT="/data/www/.backup-sales-detail-${RELEASE_ID}/${HOSTNAME_REAL}"
mkdir -p "$BACKUP_ROOT"
cp -p /data/www/dist/application/config/version.php "$BACKUP_ROOT/"
cp -p /data/www/dist/application/controllers/Report.php "$BACKUP_ROOT/"
cp -p /data/www/dist/application/views_v2/report/sales-detail.php "$BACKUP_ROOT/"
cp -p /data/www/dist/statics_v2/old/js/dist/salesDetail.js "$BACKUP_ROOT/"
cp -p /data/www/dist/statics_v2/old/js/dist/salesDetailStation.js "$BACKUP_ROOT/"
sha256sum \
"$BACKUP_ROOT/version.php" \
"$BACKUP_ROOT/Report.php" \
"$BACKUP_ROOT/sales-detail.php" \
"$BACKUP_ROOT/salesDetail.js" \
"$BACKUP_ROOT/salesDetailStation.js" \
> "$BACKUP_ROOT/backup.sha256"
执行后立即读回备份文件数量和 backup.sha256。备份失败、文件数量不为 5 或备份哈希无法校验时,停止该节点,不进入覆盖步骤。
6.2 文件上传和覆盖
文件传输必须使用已有的受控发布通道。先上传到节点临时目录,例如 /data/www/.release-sales-detail-<RELEASE_ID>/,不要直接传到 /data/www/dist。传输完成后,在远端按本次 release.sha256 校验临时目录中的五个文件,再执行覆盖。
覆盖前必须再次确认:
- 目标节点是本批次计划节点。
- 该节点的
backup.sha256已生成且校验通过。 - 临时目录五个文件的 SHA-256 与本次
release.sha256一致。 - 当前线上目标文件哈希仍等于前置核验记录,没有被其他发布改变。
确认后才覆盖五个文件。覆盖完成立即执行目标文件 SHA-256、PHP lint 和版本号读回。不要在覆盖后才补做备份。
若临时目录按项目相对路径保存,可使用以下覆盖动作;STAGE_ROOT 必须是当前节点、当前批次且已通过哈希校验的目录:
STAGE_ROOT="/data/www/.release-sales-detail-${RELEASE_ID}"
cp -p "$STAGE_ROOT/application/config/version.php" /data/www/dist/application/config/version.php
cp -p "$STAGE_ROOT/application/controllers/Report.php" /data/www/dist/application/controllers/Report.php
cp -p "$STAGE_ROOT/application/views_v2/report/sales-detail.php" /data/www/dist/application/views_v2/report/sales-detail.php
cp -p "$STAGE_ROOT/statics_v2/old/js/dist/salesDetail.js" /data/www/dist/statics_v2/old/js/dist/salesDetail.js
cp -p "$STAGE_ROOT/statics_v2/old/js/dist/salesDetailStation.js" /data/www/dist/statics_v2/old/js/dist/salesDetailStation.js
6.3 灰度和逐台放量
先选一台能被流量摘除、或能通过明确的节点路由进行验证的灰度节点。不能仅因为编号是 1 就假设它是低流量节点;本次验证没有确认负载均衡权重,因此正式发布前需要发布负责人确认流量隔离方式。
灰度节点通过后,再按“备份一台、发布一台、校验一台”的顺序处理剩余节点。任一节点失败立即停止后续节点,保留已发布节点和备份,不要继续把问题扩大到七台。
7. 发布后验证
7.1 文件和运行时
每台节点都要记录以下结果:
hostname和实际 IP;- 五个目标文件 SHA-256 是否等于本批次清单;
version.php是否为prod.2.26.72或本次新版本;Report.phpPHP lint 是否通过;- 静态资源 URL 是否携带本次版本号;
- OPcache 配置和实际请求是否已经读到新代码。
当前配置允许最多等待约 2 秒让 PHP 检查文件时间戳。等待后仍读到旧行为时,先重新确认请求命中了该节点和文件哈希;只有确认代码正确但 worker 仍持有旧缓存时,才按服务变更流程做受控 PHP-FPM reload。不要为了“保险”直接重启全部七台。
7.2 页面和接口场景
使用已授权测试账号,分别验证总部页面和服务站页面;不要把 Cookie、Token 或客户数据写入记录。
- 打开销售明细页面,确认页面 HTML 引用的
salesDetail.js带本次版本号。 - 用一个很小的正常日期范围搜索,确认 jqGrid 能正常加载,权限和合计展示没有回归。
- 使用超过六个月的日期范围点击搜索,预期接口在查询数据库前返回
status=error、error=true和msg=查询时间范围不能超过6个月,前端显示同样的提示。 - 对导出只做小范围正常场景验证;超期导出只确认错误提示,不执行两年范围导出。
- 总部账号、普通服务站账号各至少覆盖一次;确认权限不足时仍由后端权限校验拦截,前端的
rights缺失保护不能被理解为权限放开。
用户曾使用的两年范围请求属于拒绝场景,不能在线上为了验证页面而重复执行完整查询。拒绝场景的价值是确认日期校验和错误提示链路,不是验证大数据量查询。
7.3 旧浏览器缓存和静态资源
version.php 变更为新版本后,页面通过 assets() 生成新的静态资源查询参数。若浏览器仍执行旧 JS,先在 Network 中确认请求 URL 的 ver 参数和响应内容,再清理当前浏览器缓存或使用无痕窗口验证;不要直接判断为后端代码未生效。
8. 安全回滚
回滚必须使用发生故障节点自己的备份目录。发布批次的回滚条件是:
- 节点身份正确;
- 该节点备份完整且
backup.sha256可校验; - 当前五个目标文件仍全部等于本次发布版本;
- 没有后续发布覆盖这五个文件。
满足条件后,按备份中的五个文件恢复到对应线上路径,再执行五文件哈希、PHP lint、版本号和页面错误提示验证。若当前哈希已经不是本批次版本,禁止盲目覆盖,先查明后续发布或人工修改,避免把更新版本回滚掉。
恢复动作示例:
cp -p "$BACKUP_ROOT/version.php" /data/www/dist/application/config/version.php
cp -p "$BACKUP_ROOT/Report.php" /data/www/dist/application/controllers/Report.php
cp -p "$BACKUP_ROOT/sales-detail.php" /data/www/dist/application/views_v2/report/sales-detail.php
cp -p "$BACKUP_ROOT/salesDetail.js" /data/www/dist/statics_v2/old/js/dist/salesDetail.js
cp -p "$BACKUP_ROOT/salesDetailStation.js" /data/www/dist/statics_v2/old/js/dist/salesDetailStation.js
/usr/local/php/bin/php -l /data/www/dist/application/controllers/Report.php
sha256sum \
/data/www/dist/application/config/version.php \
/data/www/dist/application/controllers/Report.php \
/data/www/dist/application/views_v2/report/sales-detail.php \
/data/www/dist/statics_v2/old/js/dist/salesDetail.js \
/data/www/dist/statics_v2/old/js/dist/salesDetailStation.js
回滚只针对本次五个文件,不删除其他文件,不清理整个发布目录,不使用 git reset --hard,不直接重启全部节点。回滚后如果 OPcache 未在合理时间内读到旧文件,再按受控 PHP-FPM reload 流程处理。
9. 快速全节点回滚模式
当前默认流程是逐台回滚,优先保证安全。若要求七台尽快恢复,应在正式发布前增加“回滚就绪”阶段;发布后可以通过受控多节点执行器并行回滚,但不能承诺七台跨主机原子完成。
9.1 发布前准备
- 七台节点分别生成自己的备份目录和
backup.sha256,不能共享备份文件。 - 七台节点分别保存本节点的旧文件,并记录本批次新版本的五文件哈希,形成
rollback.manifest。 - 七台节点都通过回滚预检:备份完整、备份哈希可读、目标路径存在、当前线上哈希等于待发布版本或等于明确的旧版本。
- 回滚命令提前放入受控临时目录或发布工具,不在故障发生后临时编写和上传。
- 七台预检全部通过后,才允许进入正式发布;任一节点未准备好,不能把“快速回滚”作为本批次保障。
9.2 并行回滚门禁
发布负责人确认需要回滚后,由受控多节点执行器同时向七台节点发送同一批次回滚命令。每台节点执行顺序必须是:
确认主机身份 -> 确认当前五文件仍是本次新版本
-> 从本节点备份恢复五个文件 -> PHP lint
-> 读回五文件 SHA-256 -> 记录成功或失败原因
当前哈希不是本批次新版本时,该节点必须拒绝恢复,避免覆盖后续热修复。并行执行结束后,汇总七台结果;失败节点单独处理和复核,不能因为六台成功就宣布七台全部回滚完成。
9.3 速度和原子性边界
- 预先备份并预置回滚包后,回滚动作只包含本地五文件恢复、lint 和哈希读回,不需要重新找代码或重新打包。
- 当前
/data/www/dist是直接文件目录,没有验证存在可整体切换的 release 软链接,因此五个文件和七台主机之间都不能保证事务级原子性。 - 并行回滚只能缩短混合版本窗口,不能消除窗口;若负载均衡支持摘流,应先摘除或冻结流量,再执行并行恢复。
- OPcache 当前已验证为时间戳检查开启、频率 2 秒,通常不需要等待全部节点重启;如果实际请求仍读旧代码,只对异常节点做受控 reload。
如果现场没有可用的受控多节点执行器,退回“逐台备份—恢复—校验”的默认流程,不要临时使用未经验证的批量 SSH 脚本。
10. 发布记录模板
每次发布至少保留以下记录,不记录密码、Cookie、Token 或完整客户数据:
| 项目 | 记录内容 |
|---|---|
| 批次 | 发布单号/时间、代码分支、commit、发布人、回滚负责人 |
| 节点 | 资产名、主机名、实际代码目录、发布前/后五文件哈希 |
| 备份 | 每节点备份目录、backup.sha256 校验结果 |
| 发布 | 上传通道、临时目录、覆盖时间、失败节点和停止点 |
| 验证 | PHP lint、静态 URL 版本、正常小范围查询、超期拒绝提示、权限场景 |
| 缓存 | OPcache 读回结果、是否执行受控 reload、浏览器版本参数验证 |
| 回滚 | 是否执行、触发原因、恢复哈希、回滚后页面和接口结果 |
11. 常见错误
- 把
/data/www/dgj当成线上目录;线上本次验证的目录是/data/www/dist。 - 只备份一台节点,然后把同一份备份当成七台的回滚点。
- 只替换 JS 或只替换 PHP,造成前后端版本混合。
- 未确认流量是否摘除就把第一台节点当作灰度节点。
- 看到 HTTP 200 就认为查询成功;日期校验错误可能以 JSON 业务错误返回。
- 为了验证提示而重复执行两年大范围查询,重新触发 OOM 风险。
- 看到浏览器仍无提示就立刻改逻辑;先检查 HTML 的资源版本、Network 响应和实际命中节点。
12. 证据来源
- DGJ2 当前工作区:
/Users/zhoujiangbin/code/docker-dev-env/www/dgj2.0。 - 日期校验和接口入口:
application/controllers/Report.php的validateSalesDetailDateRange()、salesDetail_detail()、salesDetail_detail_cost()和导出方法。 - 错误 JSON 结构:
application/core/BaseController.php的splashJson()。 - 总部页面:
application/views_v2/report/sales-detail.php、statics_v2/old/js/dist/salesDetail.js。 - 服务站页面:
application/views_v2/report/station_sale_detail.php、statics_v2/old/js/dist/salesDetailStation.js。 - 静态资源版本:
application/helpers/fun_helper.php的assets()和application/config/version.php。 - 生产入口:通过 JumpServer 的 DGJ 线上资产;资产地址和权限以 JumpServer 实时列表为准。