1. 架构总览
┌─────────────┐ 授权 / 公告 / 版本更新 ┌──────────────────┐
│ 主控端 │ ───────────────────────────────▶ │ 被控端(仓库) │
│ (平台 / 管控)│ ◀─────────────────────────────── │ (业务 / 仓储) │
└─────────────┘ 版本上报 / 反馈上报 / 升级日志 └──────────────────┘
- 主控端:负责“管控面”——授权签发、版本发布与逐级下发、公告多系统下发、意见反馈归集。对外提供标准 HTTP 接口(如
api/version.php)。 - 被控端:负责“业务面”——物料、库存、盘点、统计大屏等仓储作业,以及本地业务数据。通过接口与主控协同,支持在线逐级更新。
关键契约
- 被控端
api/check_update.php上报from=当前版本,主控仅返回“下一级”版本。 - 被控端
api/do_update.php下载、校验、复制补丁并写入新版本号,可一次请求内逐级循环到最新。 - 补丁包内含
manifest.json,其from_version做跳版本硬校验(不允许跨版本直更)。
2. 技术栈与运行环境
| 项 | 要求 |
|---|---|
| 语言 | PHP 7.4 或 8.x(已验证 7.4.33 / 8.5.8) |
| 数据库 | MySQL 5.7+(已验证 5.7.44) |
| 扩展 | mysqli / curl / mbstring / zip / openssl(GD 视功能可选) |
| 运行方式 | 任意 Web 服务器(Nginx/Apache)或 php -S 内置服务器 |
| 依赖 | vendor/(PhpSpreadsheet 等,仓库导入导出使用;通过 vendor/autoload.php 引入) |
Windows 本地部署提示:若页面访问缓慢,优先检查
config.php 的 DB_HOST 是否为 localhost——Windows 下 PHP 会优先解析 IPv6 ::1 而 MySQL 通常只听 IPv4,导致连接重试变慢,改为 127.0.0.1 即可。
3. 目录结构
被控端/
├─ index.php 入口(前端页面路由)
├─ login.php / logout.php 登录登出
├─ dashboard.php 首页仪表盘(含「物料 0 库存」KPI)
├─ statistics.php 统计大屏 / 概览
├─ settings.php 系统设置(主控接入、开发者模式)
├─ feedback.php 意见反馈
├─ install.php / install.sql 安装向导与建表脚本
├─ _router.php php -S 开发路由(仅开发用)
├─ api/
│ ├─ check_update.php 检查更新(上报 from=当前版本)
│ ├─ do_update.php 在线更新(逐级循环升级引擎)
│ ├─ feedback_*.php 反馈提交/回复/删除/同步
│ └─ master_guard.php 主控接入守卫
├─ includes/
│ ├─ config.php ⚠️ 数据库凭据与环境配置(受保护,更新不下发)
│ ├─ defs.php ✅ 应用级常量/函数(可随更新下发)
│ └─ ...
├─ warehouse/ 仓储业务(导入导出依赖 vendor/autoload.php)
├─ users/ 用户/员工
├─ storage/ 运行时:上传、日志、更新备份(不上发、不发布)
├─ uploads/ 运行时上传目录
├─ vendor/ 第三方依赖(必须保留)
└─ manifest.json 当前版本清单(由发布脚本生成)
两个配置文件的分工(非常重要)
| 文件 | 内容 | 是否随在线更新下发 | 说明 |
|---|---|---|---|
includes/config.php |
DB 账号密码、站点名、SITE_VERSION、存储驱动、Session 配置 |
否(受 $block 保护) |
含敏感凭据,更新时绝不覆盖,避免冲掉用户的环境配置 |
includes/defs.php |
DEFAULT_MASTER_URL、effective_master_url()、UPDATE_CHAIN、update_chain_next() 等应用级定义 |
是 | 多处代码依赖,抽到本文件可被正常下发 |
⚠️ 铁律:新增的「应用级常量 / 函数 / 类」一律放
includes/defs.php(或新的独立可下发文件),严禁写进 includes/config.php。因为 config.php 受保护不下发,若把新常量写在那,线上旧 config.php 缺这些符号 → 页面报“未定义”、内联 JS 被破坏、功能点不动。历史上 1.3.3→1.3.4 的“系统设置无法点击”正是此坑。
4. 配置系统
includes/config.php 关键常量:
define('DB_HOST', '127.0.0.1');
define('DB_USER', 'wg_app1');
define('DB_PASS', 'App1#2026');
define('DB_NAME', 'wg_app1');
define('DB_PORT', 3306);
define('SITE_NAME', '出入库管理系统');
define('SITE_VERSION', '1.3.5');
define('STORAGE_DRIVER', 'local'); // local | cos
define('LOCAL_UPLOAD_DIR', __DIR__ . '/../uploads');
define('LOCAL_UPLOAD_URL', '/uploads');
- 支持
config_override.php(若存在则被require_once,用于环境差异覆盖)。 - 文件末尾
require_once __DIR__ . '/defs.php';引入应用级定义。 - 版本号有两个来源:
SITE_VERSION常量(代码内置)与app_version()设置项(数据库,更新成功后由save_setting('app_version', …)写入)。显示/更新判断以设置项为准;若设置项缺失则回退常量。
5. 在线更新机制(核心)
5.1 角色
| 组件 | 位置 | 职责 |
|---|---|---|
check_update.php | 被控端 api/ | 带 from=当前版本 请求主控,返回“下一级”版本与下载地址(仅用于展示) |
do_update.php | 被控端 api/ | 逐级循环升级引擎(实际执行更新) |
version.php | 主控 api/ | 按升级链返回“大于 from 的最小已发布版本”;无 from 时返回最低已发布版本(用于旧客户端自举) |
manifest.json | 每个补丁包内 | 声明 version 与 from_version,供跳版本硬校验 |
5.2 被控端 do_update.php 主流程(逐级循环)
POST api/do_update.php(仅超级管理员)
└─ while 未到最新 且 步数 < 上限:
1. 拼装更新源地址(与 check_update 一致),追加 from=当前版本
2. fetch version.php → 得到下一级 {version, download_url, from_version}
3. 下载 zip → 解压到临时目录
4. 读取 manifest.json,硬校验 manifest.from_version === 当前版本
(不等 → 拒绝“跳版本”,返回友好提示)
5. 遍历文件复制(带备份),跳过 $block 受保护文件
6. save_setting('app_version', 下一级版本) → 写日志 → 上报主控升级日志
7. 当前版本 = 下一级,进入下一轮循环
└─ 返回:{ok:true, steps, path:[...], version:最新}
旧版被控端(如 1.3.0)的
do_update.php 不带 from,会直接拉到最新版被 from_version 硬拒而无法自举。主控 version.php 的无 from 兜底改为“返回最低已发布版本(桥接包)”,旧客户端先装上新的 do_update.php 引擎,下一次更新即可自动逐级走到最新。因此旧客户端需点两次更新:第一次激活引擎,第二次一步到底。
5.3 受保护文件($block)
更新复制阶段不会覆盖以下文件(避免冲掉环境与凭据):
$block = [
'includes/config.php',
'includes/config_override.php',
'install.lock',
'.env',
'composer.lock',
];
5.4 补丁包 manifest.json 示例
{
"version": "1.3.5",
"from_version": "1.3.4",
"changelog": "新增物料 0 库存统计(首页/统计大屏/概览)。",
"generated_at": "2026-07-30"
}
6. 发布补丁流程
补丁构建脚本:发布包/build_chain.php,产出 发布包/chain_x.x.x_to_y.y.y/。
6.1 两种补丁类型
- 桥接补丁(如 1.3.0→1.3.1):仅含“链式三件套”
includes/defs.php+api/do_update.php+api/check_update.php+manifest.json。用于让旧版本获得逐级升级能力并推进版本号。 - 完整树补丁(如 1.3.3→1.3.4、1.3.4→1.3.5):完整程序树(排除
storage/vendor运行缓存等)+ 叠加三件套。保证到达该版本时为完整且正确的程序状态。
经验:完整树基线一律使用干净源码(如
被控端1),不要用混淆/历史产物当基线——混淆构建可能把函数调用当参数默认值导致致命解析错误。
6.2 文件命名
补丁 zip 使用 ASCII 文件名(如 slave_1.3.0_to_1.3.1.zip)。部分服务器对中文名 zip 下载会返回 0 字节 / 404,务必避免中文名。
6.3 校验
发布包/lint_chain.php:自包含 PHP(ZipArchive 解压 +php -l)对全部补丁内 php 文件做语法检查。发布包/verify_chain.php:校验各 manifest 与文件名一致、链式引擎存在、模拟version.php链无跳版、关键特性(如 0 库存 KPI)就位、无混淆残留。
6.4 发布到主控
- 将 5 个补丁 zip 上传到主控
uploads/。 - 后台「产品更新 / 版本」依次新建已发布记录:版本号
name与下载文件file(指向uploads/slave_*_to_*.zip)。 - 修复版
api/version.php已使无from兜底返回最低已发布版本(旧客户端自举必需)。
7. 接口契约(摘要)
| 接口 | 方法 | 关键参数 | 返回要点 |
|---|---|---|---|
主控 api/version.php | GET | from(当前版本)、auth_code | version/download_url/from_version/step/has_update/authorized;无 from 返回最低已发布版本 |
被控 api/check_update.php | GET | from、auth_code | latest/changelog/has_update |
被控 api/do_update.php | POST | 仅超级管理员 | ok/version/steps/path;逐级循环结果 |
被控 api/feedback_*.php | POST/GET | 授权相关 | 反馈提交/回复/列表(支持分页) |
被控 api/master_guard.php | — | — | 主控接入守卫 |
授权:被控端需在「系统设置」绑定主控授权码(
license_key)。未授权/过期时主控 version.php 返回 authorized:false,不下发下载地址。
8. 部署
8.1 全新安装
- 将完整安装包解压到 Web 根(保留
vendor/)。 - 确保
storage/、uploads/可写。 - 浏览器访问
install.php→ 按向导填写数据库信息(建库后用install.sql建表;导入前SET FOREIGN_KEY_CHECKS=0避免外键顺序报错)。 - 安装完成生成
install.lock。
8.2 多实例(同机多仓库)
每个被控端实例使用独立端口 / 独立库 / 独立用户 / 独立 Session 目录,避免串号:
php -S 127.0.0.1:8083 -d session.save_path=/tmp/sess_wg_app1 _router.php
php -S 127.0.0.1:8084 -d session.save_path=/tmp/sess_wg_app2 _router.php
# 各自 includes/config.php 指向独立库 wg_app1 / wg_app2 ...
8.3 在线更新(用户侧)
后台「系统设置 → 检查更新」→ 显示可升到的下一级 → 「立即更新」。新引擎下一次请求内自动逐级走到最新。
9. 二次开发指南
9.1 新增一个业务页面
- 在根目录或业务目录新建
xxx.php,顶部require_once公共头(含config.php/defs.php与登录校验)。 - 复用现有
requireLogin()/requirePermission()做权限控制(定义在includes/config.php)。 - 模板风格参考
dashboard.php/statistics.php。
9.2 新增一个 API
- 在
api/下新建api/xxx.php,开头requireLogin()+ 角色校验。 - 返回 JSON 时统一
header('Content-Type: application/json; charset=utf-8'); echo json_encode(..., JSON_UNESCAPED_UNICODE);。
9.3 新增应用级常量 / 函数
- 必须放入
includes/defs.php(或新建独立可下发文件并在各处require_once),切勿写入config.php。 defs.php顶部有if (defined('DEFAULT_MASTER_URL')) return;防御重复加载,新增定义无需重复该守卫。
9.4 资源与静态文件
- 上传文件走
LOCAL_UPLOAD_DIR(本地)或 COS(配置STORAGE_DRIVER='cos')。 - 不要在被下发文件中引用会被
$block排除的路径(config.php等)。
10. 常见问题
| 现象 | 可能原因 | 处理 |
|---|---|---|
| 检测不到主控版本 | check_update.php 源取错 / 主控未发布对应版本 | 确认默认模式使用 effective_master_url();主控需发布该版本的“下一级”记录 |
| 跨版本更新被拒 | 补丁 from_version 与当前版本不符 | 逐级更新,不可跳版;旧客户端先升到 1.3.1 激活引擎 |
| 系统设置点不开 | 旧 config.php 缺新常量(部署断裂) | 应用含 defs.php 的热修补丁;新常量一律放 defs.php |
| 页面访问慢 | DB_HOST=localhost 在 Windows 解析 IPv6 慢 | 改为 127.0.0.1 |
| 下载补丁 0 字节 / 404 | 补丁 zip 用了中文名 | 改用 ASCII 文件名(slave_*_to_*.zip) |
| 更新后功能异常 | config.php 被误覆盖 | 确认 $block 含 includes/config.php;不要整体覆盖完整包冲掉配置 |
文档随版本迭代维护;如与代码不符,以代码与
includes/defs.php 注释为准。