feat(shopro_wechat_trade): 更新插件文档并改进安装流程 - 在 README.md 中添加详细的权限问题解决方案,包括目录不可写和代码部署覆盖两种常见场景 - 添加后台文件冲突提示的处理说明 - 将 info.ini 中 state 从 0 改为 1,默认启用插件 - 在 Installer.php 中添加异常处理,防止缓存刷新失败导致安装中断 - 增强文件写入错误检测,提供更清晰的权限问题提示 - 添加 Service::refresh 的异常捕获和警告日志 ```
178 行
6.3 KiB
PHP
178 行
6.3 KiB
PHP
<?php
|
||
|
||
namespace addons\shopro_wechat_trade\library;
|
||
|
||
use app\admin\model\AuthRule;
|
||
use think\addons\Service;
|
||
use think\Cache;
|
||
use think\Db;
|
||
|
||
class Installer
|
||
{
|
||
public static function run($output = null)
|
||
{
|
||
$addonDir = ADDON_PATH . 'shopro_wechat_trade' . DS;
|
||
$name = 'shopro_wechat_trade';
|
||
|
||
self::log($output, '复制后台文件...');
|
||
foreach (['application', 'public'] as $dir) {
|
||
$source = $addonDir . $dir;
|
||
if (is_dir($source)) {
|
||
copydirs($source, ROOT_PATH . $dir);
|
||
}
|
||
}
|
||
|
||
self::log($output, '执行 install.sql...');
|
||
self::importSqlFile($addonDir . 'install.sql', $output);
|
||
|
||
self::log($output, '注册菜单...');
|
||
self::installMenu($output);
|
||
|
||
\app\common\library\Menu::enable('shopro_wechat_trade/config');
|
||
\app\common\library\Menu::enable('shopro_wechat_trade/sync');
|
||
self::grantRulesToShoproGroups($output);
|
||
self::clearMenuCache();
|
||
self::setAddonState($name, 1, $output);
|
||
|
||
try {
|
||
(new WxOrderPathService())->updatePath();
|
||
self::log($output, '已配置微信订单详情路径');
|
||
} catch (\Exception $e) {
|
||
self::log($output, ' [warn] 订单详情路径配置失败: ' . $e->getMessage());
|
||
if (strpos($e->getMessage(), '10060035') !== false) {
|
||
self::log($output, ' [warn] 10060035 常见原因:小程序线上/提审版尚未包含 pages/order/detail,请先发布小程序后再执行 php think shopro_wechat_trade:init-path');
|
||
self::log($output, ' [warn] 或在小程序后台手动填写 path: pages/order/detail?id=${商品订单号}');
|
||
} elseif (strpos($e->getMessage(), '10060034') !== false) {
|
||
self::log($output, ' [warn] 请先在小程序后台签署订单中心授权协议');
|
||
}
|
||
}
|
||
|
||
try {
|
||
Service::refresh();
|
||
} catch (\Exception $e) {
|
||
self::log($output, ' [warn] 插件缓存刷新失败: ' . $e->getMessage());
|
||
}
|
||
self::log($output, '安装完成: /admin/shopro_wechat_trade/config');
|
||
}
|
||
|
||
public static function setAddonState($name, $state, $output = null)
|
||
{
|
||
$file = ADDON_PATH . $name . DS . 'info.ini';
|
||
if (!is_file($file)) {
|
||
return false;
|
||
}
|
||
$content = file_get_contents($file);
|
||
$state = intval($state);
|
||
if (preg_match('/^state\s*=\s*\d+/m', $content)) {
|
||
$content = preg_replace('/^state\s*=\s*\d+/m', 'state = ' . $state, $content);
|
||
} else {
|
||
$content = rtrim($content) . "\nstate = " . $state . "\n";
|
||
}
|
||
if (file_put_contents($file, $content, LOCK_EX) === false) {
|
||
throw new \RuntimeException('无法写入 ' . $file . ',请检查 addons 目录权限(勿用 root 上传后未 chown www)');
|
||
}
|
||
\think\Config::set($name, null, 'addoninfo');
|
||
\think\Config::set('addon-info-' . $name, null);
|
||
|
||
if ($state === 1) {
|
||
try {
|
||
Service::refresh();
|
||
} catch (\Exception $e) {
|
||
self::log($output, ' [warn] Service::refresh 失败: ' . $e->getMessage());
|
||
self::log($output, ' [warn] 请修复 runtime/cache、application/extra/addons.php、public/assets/js/addons.js 写权限后重跑 install');
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
public static function installMenu($output = null)
|
||
{
|
||
$menuFile = ADDON_PATH . 'shopro_wechat_trade' . DS . 'config' . DS . 'menu.php';
|
||
if (!is_file($menuFile)) {
|
||
return false;
|
||
}
|
||
$menu = include $menuFile;
|
||
if (!$menu) {
|
||
return false;
|
||
}
|
||
$parent = self::resolveMenuParent();
|
||
self::recreateMenu($menu, $parent, 'shopro_wechat_trade/config', ['shopro_wechat_trade']);
|
||
\app\common\library\Menu::refresh('shopro_wechat_trade', $menu);
|
||
return $parent;
|
||
}
|
||
|
||
protected static function resolveMenuParent()
|
||
{
|
||
foreach (['shopro/config', 'shopro'] as $name) {
|
||
$rule = AuthRule::getByName($name);
|
||
if ($rule && $rule['status'] === 'normal') {
|
||
return $name;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
protected static function recreateMenu(array $menu, $parent, $rootName, array $legacyNames = [])
|
||
{
|
||
foreach (array_merge($legacyNames, [$rootName]) as $name) {
|
||
\app\common\library\Menu::delete($name);
|
||
}
|
||
\app\common\library\Menu::create($menu, $parent);
|
||
}
|
||
|
||
protected static function grantRulesToShoproGroups($output = null)
|
||
{
|
||
$ruleIds = array_merge(
|
||
\app\common\library\Menu::getAuthRuleIdsByName('shopro_wechat_trade/config'),
|
||
\app\common\library\Menu::getAuthRuleIdsByName('shopro_wechat_trade/sync')
|
||
);
|
||
if (!$ruleIds) {
|
||
return;
|
||
}
|
||
$groups = Db::name('auth_group')->where('name', 'like', '%shopro%')->select();
|
||
foreach ($groups as $group) {
|
||
$rules = array_filter(explode(',', $group['rules']));
|
||
$merged = array_unique(array_merge($rules, $ruleIds));
|
||
Db::name('auth_group')->where('id', $group['id'])->update(['rules' => implode(',', $merged)]);
|
||
}
|
||
}
|
||
|
||
protected static function importSqlFile($file, $output = null, $ignoreError = false)
|
||
{
|
||
if (!is_file($file)) {
|
||
return;
|
||
}
|
||
$prefix = config('database.prefix');
|
||
$sql = str_replace('__PREFIX__', $prefix, file_get_contents($file));
|
||
$statements = array_filter(array_map('trim', explode(';', $sql)));
|
||
foreach ($statements as $statement) {
|
||
if ($statement === '') {
|
||
continue;
|
||
}
|
||
try {
|
||
Db::execute($statement);
|
||
} catch (\Exception $e) {
|
||
if (!$ignoreError) {
|
||
self::log($output, ' [warn] SQL: ' . $e->getMessage());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
protected static function clearMenuCache()
|
||
{
|
||
try {
|
||
Cache::rm('__menu__');
|
||
} catch (\Exception $e) {
|
||
// ignore
|
||
}
|
||
}
|
||
|
||
protected static function log($output, $message)
|
||
{
|
||
if ($output) {
|
||
$output->writeln($message);
|
||
}
|
||
}
|
||
}
|