客联盈 常见问题排查指南
面向部署、配置、使用过程中的高频问题,提供快速排查路径与解决方案。
一、部署问题
1.1 宝塔面板安装
Q:外网打不开宝塔面板
| 可能原因 | 排查方式 | 解决 |
|---|---|---|
| 安全组未放行面板端口 | 阿里云控制台 → 安全组 → 入方向规则 | 放行面板端口(默认 8888,建议改后放行新端口),仅限办公 IP |
| 系统防火墙拦截 | firewall-cmd --list-ports 或 iptables -L | 放行面板端口或安装期临时关闭防火墙 |
| 面板未启动 | SSH 执行 bt status | bt start 启动面板 |
| 端口记错 | SSH 执行 bt default | 查看当前面板地址、账号、密码 |
Q:安装宝塔报冲突 / 端口占用
原因:系统不干净,已预装 Nginx/Apache/PHP/MySQL。
解决:重装干净系统(推荐 CentOS 7.9 或 Alibaba Cloud Linux 3),不要选预装 LNMP 的应用镜像。只选一种方式装宝塔(控制台扩展程序 或 SSH 脚本,二选一)。
Q:curl/wget 下载安装脚本失败
原因:服务器出网受限或官网下载地址临时不可用。
解决:
- 检查出网
curl -I https://www.bt.cn - 换官网最新安装命令(https://www.bt.cn/linux)
- 阿里云 ECS 可改用控制台"扩展程序"一键安装
1.2 站点访问
Q:打开首页报 404 Not Found
| 可能原因 | 排查方式 | 解决 |
|---|---|---|
站点根目录未指向 server/public/ | 宝塔 → 网站 → 设置 → 网站目录 | 根目录改为 /www/wwwroot/klinkwin-mall/server/public |
| 伪静态未配置 | 查看站点"伪静态"是否为空 | 按文档配置 ThinkPHP 伪静态(见下方) |
| 代码上传路径错误 | 确认 server/public/index.php 存在 | 重新上传/解压代码到正确位置 |
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=/$1 last;
}
}
Q:打开任何 PHP 页面报 502 Bad Gateway
| 可能原因 | 排查方式 | 解决 |
|---|---|---|
| PHP-FPM 未启动 | 宝塔 → 软件商店 → PHP-8.1 → 状态 | 重启 PHP-8.1 |
| sock 路径错误 | 站点配置中 fastcgi_pass 指向 | 确认为 PHP-8.1 的 sock(如 unix:/tmp/php-cgi-81.sock) |
open_basedir 拦截 | 站点设置 → 网站目录 | 关闭 open_basedir 或放宽到能访问 server 目录 |
Q:前端页面刷新子路由报 404
原因:Nginx 未配置 SPA 的 try_files。
解决:在伪静态中补充 SPA 配置:
location ^~ /saas-admin/ {
try_files $uri $uri/ /saas-admin/index.html;
}
location ^~ /shop/ {
try_files $uri $uri/ /shop/index.html;
}
location ^~ /pc/ {
try_files $uri $uri/ /pc/index.html;
}
location ^~ /mobile/ {
try_files $uri $uri/ /mobile/index.html;
}
Q:上传文件报 413 Request Entity Too Large
原因:Nginx client_max_body_size 或 PHP upload_max_filesize 过小。
解决:
- 站点配置中加
client_max_body_size 50M; - PHP-8.1 → 配置修改:
upload_max_filesize = 50M、post_max_size = 60M
1.3 安装向导
Q:安装向导打不开,报 Warning: include() 或空白页
原因:vendor/ 目录不存在或缺失。
解决:
cd /www/wwwroot/klinkwin-mall/server
/www/server/php/81/bin/php /usr/bin/composer install --no-dev
Q:/install/ 目录访问报 403
原因:install/ 下无 index.php,Nginx 默认 index 不匹配。
解决:在站点 Nginx 配置中增加:
location /install/ {
index install.php;
try_files $uri $uri/ /install/install.php?$query_string;
}
Q:环境检测 session.auto_start 不通过
原因:php.ini 中写的是 session.auto_start = Off(字符串),检测期望数字 0。
解决:PHP-8.1 → 配置修改 → 改为 session.auto_start = 0。
Q:安装向导"数据库连接失败 1045 Access denied"
| 可能原因 | 解决 |
|---|---|
.env 用户名/密码错误 | 核对宝塔"数据库"面板中对应库的用户名和密码 |
| 使用 root 账号 | 宝塔建站分配的独立库用户,不要用 root |
| MySQL 认证插件不兼容 | my.cnf 设 default-authentication-plugin=mysql_native_password |
Q:安装完成后想重新安装
解决:删除 server/config/install.lock 文件(慎用,会清空数据库)。
1.4 数据库连接
Q:Illegal mix of collations(排序规则不一致)
原因:数据库默认排序规则与表 SQL 指定的排序规则不一致。
解决:
- MySQL 配置中设
collation-server=utf8mb4_general_ci - 重建数据库(必须
DROP DATABASE后CREATE DATABASE,已建库的排序不会因改配置而自动变更) - 重新运行安装向导
Q:GROUP BY 聚合报错 ONLY_FULL_GROUP_BY
原因:MySQL sql_mode 包含 ONLY_FULL_GROUP_BY。
解决:在 MySQL 配置的 [mysqld] 中设:
sql_mode=STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
保存后重载配置/重启 MySQL。
1.5 SSL/HTTPS 证书
Q:宝塔申请 Let's Encrypt 证书失败
| 可能原因 | 解决 |
|---|---|
| 域名未解析到服务器 IP | 先确认 ping 域名 返回服务器 IP,再申请证书 |
| 80 端口未放行 | 安全组 + 系统防火墙放行 80 端口(Let's Encrypt 验证需要 HTTP) |
| 网站目录指向错误 | 验证时 Nginx 会写临时文件到网站根目录,确保根目录可写 |
| 泛域名证书需 DNS 验证 | 泛域名(*.mall.example.com)不支持 HTTP 验证,需用 DNS 验证或改用 Cloudflare/阿里云 DNS 插件 |
Q:HTTPS 访问后页面显示"混合内容"(Mixed Content)
原因:页面内引用了 HTTP 资源(图片、CSS、JS)。
排查:浏览器 F12 → Console 查看 Mixed Content 警告,找到具体 HTTP 资源 URL。
解决:
- 资源为本站资源:强制走 HTTPS,Nginx 配置中加入
add_header Content-Security-Policy "upgrade-insecure-requests"; - 资源为外部 CDN:改用 HTTPS 链接
- 后端
.env中APP_URL和DOMAIN改为https://开头
Q:证书即将过期 / 忘记续签
解决:宝塔 → 网站 → 设置 → SSL → 证书 → 续签。建议开启宝塔面板的自动续签计划任务。
Q:小程序要求 HTTPS 且域名需备案
关键:小程序上线前提条件:
- 域名已 ICP 备案
- 配置 HTTPS 证书(TLS 1.2+)
- 在小程序后台 → 开发管理 → 服务器域名中添加 request/upload/download 合法域名
1.6 升级与更新
Q:升级包解压后覆盖了 .env 导致站点无法访问
原因:升级包覆盖了 server/.env 导致数据库/Redis 连接信息丢失。
解决:
- 升级前必须备份
.env文件:
cp /www/wwwroot/klinkwin-mall/server/.env /tmp/.env.bak
- 升级后核对
.env中数据库、Redis 密码、租户模式等关键配置是否完整 - 若已被覆盖,从备份恢复
.env
Q:升级后前端页面白屏/报错
| 可能原因 | 解决 |
|---|---|
| 前端 dist 未更新 | 升级包中覆盖 public/shop/、public/pc/、public/mobile/ |
| 浏览器缓存旧 JS | 强制刷新(Ctrl+Shift+R),或在 Nginx 中给静态资源加版本号 |
| 后端 API 变更未同步 | 确认 migrate.sql 已执行,后端路由/接口无 Breaking Changes |
Q:升级后数据库迁移脚本执行失败
排查:
- 确认 MySQL 版本满足
requires_mysql要求(≥ 8.0) - 查看迁移脚本错误信息,确认无重复建表/字段冲突
- 若迁移脚本终止一半,不要重新执行,先用备份恢复数据库后重试
Q:升级失败如何回滚
标准回滚流程:
# 1. 恢复数据库
mysql -u root -p klw_mall < backups/pre_upgrade_xxx/db.sql
2. 恢复上传文件
tar xzf backups/pre_upgrade_xxx/uploads.tar.gz -C /www/wwwroot/klinkwin-mall/server/public/uploads/
3. 恢复代码(若有备份包)
用旧版升级包反向覆盖,或从 Git 回退
4. 清缓存 + 重载服务
rm -rf /www/wwwroot/klinkwin-mall/server/runtime/cache/*
/etc/init.d/php-fpm-81 reload && nginx -s reload
关键:升级前务必做完整备份(数据库 + uploads + .env),不要在半升级状态上线。
二、环境配置问题
2.1 PHP 相关
Q:composer install 或 php think 报 disabled function 错误
原因:宝塔默认禁用函数列表包含了 ThinkPHP/Composer 需要的函数。
解决:PHP-8.1 → 设置 → 禁用函数,删除以下项:
putenv, proc_open, proc_get_status, proc_close, pcntl_signal,
pcntl_alarm, pcntl_fork, exec, shell_exec, passthru, popen,
symlink, readlink, link
保存并重载 PHP。
Q:验证码/二维码/图片处理报 Call to undefined function imagecreate
原因:PHP gd 扩展未安装或依赖不全。
解决:PHP-8.1 → 安装扩展 → 重装 gd。终端验证:
/www/server/php/81/bin/php -m | grep -i gd
Q:站点选错 PHP 版本
解决:宝塔 → 网站 → 设置 → PHP 版本 → 改为 PHP-81。
Q:PHP 必装扩展清单
需确认以下扩展均已安装(PHP-8.1 → 设置 → 安装扩展):
| 扩展 | 用途 |
|---|---|
pdo_mysql / mysqli | 数据库 |
gd | 图片、验证码 |
zip | Composer / 压缩 |
bcmath | 金额计算 |
fileinfo | 上传 / MIME |
intl | 国际化 |
opcache | 性能 |
mbstring | 字符串 |
curl / openssl | HTTP / HTTPS |
2.2 MySQL 相关
Q:MySQL 标准配置(klinkwin 项目统一要求)
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
sql_mode=STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
Q:忘记 MySQL root 密码
# 1. 停 MySQL
/etc/init.d/mysqld stop
2. 跳过权限启动
/www/server/mysql/bin/mysqld --skip-grant-tables --user=mysql &
sleep 3
3. 无密码登录
mysql -u root
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码';
FLUSH PRIVILEGES;
EXIT;
# 4. 正常重启
pkill -f skip-grant-tables
/etc/init.d/mysqld restart
Q:宝塔安装 MySQL 后查看初始密码
sqlite3 /www/server/panel/data/default.db "SELECT mysql_root FROM config LIMIT 1;"
2.3 Redis 相关
Q:Redis 必须设密码
解决:宝塔 → 软件商店 → Redis → 设置 → 配置修改 → 增加 requirepass 你的强密码 → 重启。.env 中 [REDIS] PASSWORD 填同一密码。
Q:Redis 连接失败
排查:
redis-cli -a 密码 ping确认服务正常- 确认安全组未放行 6379 端口(Redis 仅本机访问)
.env中 HOSTNAME 应为127.0.0.1
2.4 定时任务与队列
Q:定时任务执行了但没效果
原因:CLI PHP 版本不对(走了系统默认的 7.x 或 8.0)。
解决:计划任务中必须写 PHP 8.1 绝对路径:
cd /www/wwwroot/klinkwin-mall/server
/www/server/php/81/bin/php think crontab >> runtime/crontab.log 2>&1
验证:先加 date >> /tmp/btcron.log 确认每分钟触发,再看 runtime/crontab.log。
Q:队列任务执行不起来
| 可能原因 | 排查方式 |
|---|---|
| Supervisor 启动用户错误 | 启动用户应为 www,非 root |
| Redis 连不上 | redis-cli -a 密码 ping |
| 队列堆积 | redis-cli llen queues:default |
[program:klinkwin-mall-queue]
command=/www/server/php/81/bin/php think queue:work --queue default --tries 3
directory=/www/wwwroot/klinkwin-mall/server
user=www
autostart=true
autorestart=true
Q:定时任务清单
所有定时任务由 php think crontab 统一调度,读取 klw_dev_crontab 表按 cron 表达式分发:
| 命令 | 用途 |
|---|---|
orderClose | 超时未支付订单自动取消+退款 |
orderConfirm | 发货超时自动确认收货 |
orderRefund | 处理待退款售后单 |
settlement | 按店铺结算周期执行结算 |
msDeal | 秒杀活动自动开始/结束 |
ptDeal | 拼团自动成团/失败退款 |
distribution | 分销佣金结算 |
userWithdraw | 用户提现处理 |
coupon | 优惠券过期标记 |
wechat_mini_express_send_sync | 微信发货同步 |
packageDeal | SaaS 套餐到期处理 |
2.5 前端构建与访问
Q:SaaS 和独立部署构建命令的区别
| 模式 | 构建命令 | 产物 | 前端租户模式 |
|---|---|---|---|
| SaaS | pnpm build:saas | saas-admin + shop + pc + mobile | VITE_TENANT_MODE=runtime |
| 独立 | pnpm build:standalone | shop + pc + mobile(无 saas-admin) | VITE_TENANT_MODE=build + VITE_TENANT_SHOP_ID |
Q:独立部署 shop_id 不是 1
先修改 pc/.env.standalone 和 uniapp/.env.standalone 中的 VITE_TENANT_SHOP_ID,以及后端 .env 的 TENANT_SHOP_ID,再执行 pnpm build:standalone。
Q:前端页面空白/白屏
| 可能原因 | 排查 |
|---|---|
| 前端 dist 未上传 | 确认 server/public/shop/ 等目录存在且含 index.html |
| 构建模式不匹配 | SaaS 部署用 build:saas,独立用 build:standalone |
| 浏览器缓存 | 强制刷新(Ctrl+Shift+R)或清缓存 |
Q:生产机不要装 Node
前端由交付方/CI 在本机构建后上传 dist 产物,生产机只装 Nginx + PHP + MySQL + Redis。
三、使用操作问题
3.1 租户与店铺
Q:SaaS 模式 vs 独立部署模式的区别
| SaaS | 独立部署 | |
|---|---|---|
.env | TENANT_MODE = saas | TENANT_MODE = standalone + TENANT_SHOP_ID = 1 |
| 域名 | 主域名 + 泛域名 | 单域名 |
| 租户识别 | 按域名自动识别 shop_id | 固定 shop_id |
| 支付配置 | 商户保存后需平台审核 | 保存即生效,免审核 |
| 套餐 | 按套餐控制功能模块 | 全量可用 |
Q:新建租户后 shop 端无法登录
排查:
- 确认租户状态为"启用"
- 确认域名已绑定且 DNS 已解析到服务器 IP
- 确认套餐已开通且未过期
- 确认管理员账号密码正确
Q:租户域名总落到默认店铺
排查:
- 检查
klw_tenant_domain表中域名绑定是否正确 - 确认泛域名解析
*.mall.example.com已配置 - 检查中间件缓存是否过期
Q:独立部署时访问 /saas-admin/ 报 404
这是正常的。独立部署不包含 saas-admin,商户后台入口为 /shop/。
3.2 支付配置
Q:SaaS 模式下支付配置保存后不生效
原因:SaaS 模式下商户支付配置需平台审核通过后才生效。
解决:saas-admin → 租户管理 → 配置审核 → 审核通过。
Q:独立部署支付配置不生效
排查:
- 确认
.env中TENANT_MODE = standalone - 独立模式下支付配置保存即生效,无需审核
- 检查微信/支付宝商户号、API 密钥、证书路径是否正确
Q:支付回调无响应
排查:
- 服务器出网是否正常:
curl -v https://api.mch.weixin.qq.com - 回调 URL 是否在支付平台白名单中
- 检查
runtime/log/下的支付日志 - 确认定时任务和队列是否正常运行
3.3 营销活动
Q:秒杀/拼团/砍价活动创建后前端不显示
| 可能原因 | 解决 |
|---|---|
| 活动未提交审核 | 在 shop 端提交审核(SaaS 模式需平台审核通过) |
| 活动未开启 | 点击"开启活动" |
| 活动时间未到 | 检查活动开始/结束时间 |
| 套餐未开通该模块 | 联系平台升级套餐 |
| 前端缓存 | 强制刷新页面 |
Q:秒杀下单提示"不在秒杀时间段内"
原因:秒杀活动有预热期,预热期间不可下单,仅到活动正式开始时间后才可购买。
Q:拼团失败后未自动退款
排查:
- 确认定时任务
php think crontab正常运行 - 检查
runtime/crontab.log是否有ptDeal相关日志 - 确认拼团时效已过
Q:优惠券领取后无法使用
排查:
- 检查优惠券有效期(使用时间和发放时间是独立的)
- 检查是否满足使用门槛(满减金额/指定商品)
- 是否与其他优惠互斥
- 优惠券是否已被作废
3.4 订单与售后
Q:订单超时未自动取消
排查:
- 确认定时任务
php think crontab每分钟正常执行 - 检查"交易设置"中的订单超时自动取消时间
runtime/crontab.log中查看orderClose日志
Q:发货后用户未自动确认收货
排查:
- 确认
orderConfirm定时任务正常 - 检查"交易设置"中的自动确认收货时限
Q:售后审核通过后未自动退款
排查:
- 确认
orderRefund定时任务正常 - 检查支付商户号余额是否充足
- 查看退款记录中的退款日志
3.5 分销与提现
Q:分销佣金未结算
排查:
- 确认订单已完成且超过售后期
- 确认
distribution定时任务正常 - 检查分销设置中的"结算时机"(订单完成后 N 天)
- 确认商品已参与分销
Q:分销员提现申请审核后未到账
排查:
- 审核通过后还需"确认打款"操作
- 确认打款方式(微信/支付宝)配置正确
- 检查微信商户号余额
- 查看
userWithdraw定时任务日志
Q:提现状态与定时任务状态混淆
重要:提现状态(0/1/2/3)和定时任务处理状态(1/2/3)是两套独立枚举,切勿混用。
3.6 短信与通知
Q:短信发送失败
| 可能原因 | 排查 |
|---|---|
| 短信签名/模板未审核 | 登录短信平台(阿里云/腾讯云)确认签名和模板状态为"已通过" |
| 短信余额不足 | 登录短信平台确认账户余额 |
| 服务器出网受限 | curl -v 测试短信 API 网关连通性 |
| 短信配置未填写 | saas-admin/shop 后台 → 系统设置 → 短信配置中填写 AK/SK/签名 |
| 模板变量与传参不匹配 | 检查模板变量数量和顺序是否与 API 传参一致 |
Q:微信模板消息/订阅消息不推送
排查:
- 确认微信公众号/小程序已认证,模板消息功能已开通
- 检查模板 ID 是否配置正确(saas-admin → 微信设置 → 模板消息)
- 确认用户已关注公众号(模板消息)或已授权订阅(订阅消息)
- 查看
runtime/log/中微信 API 调用的返回结果
Q:支付回调通知不触发
排查:
- 服务器出网是否正常:
curl -v https://api.mch.weixin.qq.com - 回调 URL 是否在支付平台白名单中(微信商户平台 → 产品中心 → 开发配置)
- 检查
runtime/log/下的支付日志 - 确认队列
php think queue:work正常运行
3.7 小程序与 H5
Q:小程序真机调试无法请求接口
| 可能原因 | 解决 |
|---|---|
| 域名未配置 HTTPS | 确保证书有效且 TLS ≥ 1.2 |
| 域名未添加到合法域名 | 小程序后台 → 开发管理 → 服务器域名 → 添加 request 合法域名 |
| 域名未 ICP 备案 | 小程序强制要求已备案域名,无备案只能开发工具调试 |
| 合法域名每月限改 5 次 | 谨慎修改,建议用测试域名+正式域名分开配置 |
Q:H5 支付无法唤起微信/支付宝
排查:
- 微信 H5 支付需在微信商户平台开通"H5 支付"产品
- H5 支付域名需在商户平台配置"H5 支付域名"(支付授权目录)
- 支付宝 H5 需在支付宝开放平台开通"手机网站支付"
- 微信内 H5 需使用 JSAPI 支付(需微信认证服务号),非微信浏览器用 H5 支付
Q:H5 分享链接后页面空白
排查:
- 确认 Nginx 已配置 SPA
try_files(见 1.2 节) - 微信内置浏览器对 URL 有长度限制,避免过长参数
- 分享链接中的参数是否正确编码(
encodeURIComponent)
Q:小程序上传体验版后部分功能异常
排查:
- 体验版需在小程序后台添加"体验成员"
- 确认服务器域名(request/upload/download)均已配置
- 检查业务域名配置(webview 页面需配置业务域名)
- 确认
appid和secret与小程序后台一致
四、踩坑经验(编码层面)
以下为开发过程中已踩坑并修复的高频问题,新增功能时务必遵守。
| 问题 | 根因 | 修复方案 |
|---|---|---|
| 营销活动并发竞态(多人同时发起/助力) | 查重逻辑在事务外,高并发下重复写入 | 将查重移入 Db::startTrans() + lock(true) 事务内 |
| 编辑时 status 被意外覆盖 | allowField 中包含 status/audit_status | 从 allowField 中移除这两个字段 |
| 编辑商品时 stock 被重置 | onBeforeWrite 全局执行 stock=total_stock,覆盖了已售库存 | 仅 add 分支执行 stock = total_stock |
| 访问器返回 null 导致校验异常 | 访问器 default 分支无 return | 所有访问器 default 必须有 return |
| 买家下单参数丢失 | 下单经过中转页导致体验差且参数丢失 | uniapp 下单直接跳 goods_order,参数含 add_type + 活动 id |
| 新增套餐 key 后老租户报错 | 老租户 modules JSON 缺少新 key | TenantPackageService::getByShopId() 做默认值回退 |
五、快速诊断命令集
# === 宝塔面板 ===
bt default # 查看面板地址/账号/密码
bt status # 查看面板运行状态
bt 14 # 查看面板错误日志
=== PHP ===
/www/server/php/81/bin/php -v # PHP 版本
/www/server/php/81/bin/php -m # 已安装扩展列表
/www/server/php/81/bin/php -m | grep -iE 'gd|zip|bcmath|fileinfo|intl|opcache|pdo_mysql'
=== MySQL ===
mysql -u root -p # 登录 MySQL
SHOW VARIABLES LIKE 'sql_mode'; # 查看 sql_mode
SHOW VARIABLES LIKE 'collation%'; # 查看排序规则
SHOW VARIABLES LIKE 'character_set%'; # 查看字符集
=== Redis ===
redis-cli -a 密码 ping # 测试连接
redis-cli -a 密码 llen queues:default # 查看队列堆积
=== 服务状态 ===
/etc/init.d/nginx status # Nginx 状态
/etc/init.d/php-fpm-81 status # PHP-FPM 状态
/etc/init.d/mysqld status # MySQL 状态
supervisorctl status # Supervisor 状态
=== 定时任务 ===
cat /www/wwwroot/klinkwin-mall/server/runtime/crontab.log # 定时任务日志
tail -f /www/wwwroot/klinkwin-mall/server/runtime/log/202608/*.log # 应用日志
=== 网络 ===
curl -I http://你的域名/ # 测试 HTTP 响应
curl -v https://api.mch.weixin.qq.com # 测试支付网关连通性
netstat -tlnp | grep -E '80|443|3306|6379' # 查看端口监听