部署与运维
目标机:与 vip/test 同一台阿里云主机(1.6G 内存、PHP 8.2 无 fileinfo、禁 exec/pcntl、MySQL 未启用)。 这些约束决定了本系统的形态:SQLite、不能自起常驻进程、异步靠 crontab、上传校验不用 fileinfo。
目录与权限
/www/wwwroot/wx.onzx.cn
├── app/ config/ database/ tools/ # 代码,站点根之外,不可被 web 直接执行
├── public/ # ← nginx 站点根必须指到这里
│ ├── index.php # 唯一入口
│ ├── assets/… # css/js/img
│ └── MP_verify_xxx.txt # 微信域名校验文件(公开)
└── storage/ # 0750,属主 www
├── wxhub.db # SQLite(WAL)
├── keys/ # 0700:app.key、wxpay/<id>/*.pem
├── logs/ # app-YYYYMMDD.log、cron.log
└── installed.lock
nginx 站点配置要点
仓库里有一份现成的:deploy/nginx/wx.onzx.cn.conf 与 deploy/nginx/rewrite-wx.onzx.cn.conf。
宝塔面板生成的配置只需改四处:
root指向…/wx.onzx.cn/public(面板「网站根目录」或改配置文件)。- 加 443 server 块 + 证书,80 只保留 ACME 验证与 301。
- 伪静态用
try_files $uri $uri/ /index.php?$query_string;,并限制只有 index.php 可执行。 .user.ini的open_basedir覆盖到站点根(含 app/storage),否则读不到密钥与数据库。
server {
listen 443 ssl http2;
server_name wx.onzx.cn wx.123xl.cn;
root /www/wwwroot/wx.onzx.cn/public;
index index.php;
ssl_certificate /www/server/panel/vhost/cert/wx.onzx.cn/fullchain.pem;
ssl_certificate_key /www/server/panel/vhost/cert/wx.onzx.cn/privkey.pem;
include /www/server/panel/vhost/rewrite/wx.onzx.cn.conf;
location ~ ^/(app|config|database|storage)/ { return 404; }
location ~ .*\.(php)$ {
# 只有 index.php 允许执行,别的 .php 一律 404
if ($uri != "/index.php") { return 404; }
include enable-php-82.conf;
}
}
证书
# HTTP-01 签发(两个域名一起签,SAN 证书)
acme.sh --issue -d wx.onzx.cn -d wx.123xl.cn -w /www/wwwroot/wx.onzx.cn/public --keylength ec-256
acme.sh --install-cert -d wx.onzx.cn --ecc \
--fullchain-file /www/server/panel/vhost/cert/wx.onzx.cn/fullchain.pem \
--key-file /www/server/panel/vhost/cert/wx.onzx.cn/privkey.pem \
--reloadcmd "nginx -t && nginx -s reload"
别名域名不一定签得下来。
123xl.cn 的备案接入商是腾讯云,把它的子域解析到阿里云主机后,
阿里云可能因"未接入备案"拦截 80/443,导致 ACME 验证失败。
出现这种报错就不要硬签第二个域名 —— 中台只需要主域名 wx.onzx.cn 可用,
别名域名可以只作为内部备用(或改用腾讯云侧的接入备案)。
计划任务(必须从宝塔面板添加)
/www/server/php/82/bin/php /www/wwwroot/wx.onzx.cn/tools/cron_worker.php \
>> /www/wwwroot/wx.onzx.cn/storage/logs/cron.log 2>&1
每 1 分钟。手写进系统 crontab 的条目在面板里看不到、可能被覆盖,所以一定要走面板「计划任务」。 这条是整个系统唯一的异步出口:不跑它,回调、消息、关单、查单全部停摆(概览页会有红色心跳提示)。
发布
bash tools/deploy.sh # 同步到 /root/wxhub-dev + PHP 8.2 lint + selfcheck
bash tools/deploy.sh --prod # 覆盖生产(保留 storage/、config/local.php、.user.ini、public/MP_verify_*.txt)
bash tools/deploy.sh --migrate # 顺带跑 database/migrations
发布是 rsync --delete:只存在于生产机的文件必须放进 exclude,
否则下一次发布会把它冲掉(vip 项目真发生过:导入的头像被删干净)。所以校验文件与 storage 都在排除列表里。
备份与恢复
# 一致性快照(不要用 cp 拷正在写的 SQLite)
sqlite3 storage/wxhub.db ".backup '/root/wxhub-pre-$(date +%Y%m%d-%H%M).db'"
tar -czf /root/wxhub-keys-$(date +%Y%m%d).tgz storage/keys # 0600,离线保存
storage/keys/app.key是所有密文的唯一钥匙:丢了库里所有 AppSecret/APIv3 都解不开,只能全部重置。- 商户私钥 5 年不可续、最多 3 份、丢了找不回 —— 每次新增通道都离线备份一份。
- 密钥备份文件不要放进仓库、不要放进
public/。
轮换清单
| 要换的东西 | 怎么做 | 连带影响 |
|---|---|---|
| AppSecret | 微信开发者平台重置 → 后台「微信账号」填新值 | 中台会自动清掉该账号的 token 缓存;共用该号的其他系统也要同步更新 |
| APIv3 密钥 | 商户平台重设 → 通道页填新值 | 旧回调会解密失败,重设前后不要有在途订单 |
| 站点接入密钥 | 站点详情页「重置」 | 旧密钥立刻失效,会连续 401;约好时间一起换 |
| 入口域名 | 设置 → 域名与限额 | 必须先在新域名上传 MP_verify 并在 mp 后台改授权域名(每月限 5 次),再改这里 |
升级注意
- 迁移只前进:
database/migrations/NNNN_*.sql,执行前自动快照备份。 - 先发迁移再发代码,避免"新代码跑在旧表上"。
- SQLite 单写锁:worker 与 php-fpm 并发时靠 WAL +
busy_timeout+ 批次上限撑住, 别把msg_batch/cb_batch调到几百。
本页内容随代码一起版本化(V0.1.0)。发现文档与实际行为不一致,请当缺陷报告。