feat: 公共表更名 common/member 前缀 + 插件命令自动加载 + 版本 1.1.1
- 18 张公共表更名(addon/attachment/configure/links/spider_log/spider_stat/sms/notice/ad/task/prop/medal/help/card -> common_*,member_wallets->member_wallet,score_rule/score_log -> member_*,addon_config->common_addonconf),模型全部对齐新表名,Db 直引用清零 - Attachment 模型补 表名绑定,修复富文本上传查 wxapp_attachment 1146 隐患 - install.sql + 迁移 SQL:backend_admin/backend_role delete_at 默认 0,修复软删除(NULL != 0)误过滤导致后台菜单为空 - AppService::boot() 支持插件 info.php 声明 commands 自动注册插件命令(psr-4 自动加载,坏类名自动跳过) - 各插件(haonav/mqttbroker/wxchat/articles/blog/forum 等)字段与配置同步调整 - 框架版本 1.1.0 -> 1.1.1
This commit is contained in:
@@ -214,6 +214,12 @@ class AddonDevService
|
||||
$data[] = $item;
|
||||
}
|
||||
$this->writeReturnArray($this->addonDir . 'config.php', $data);
|
||||
|
||||
// 设计器改了 config.php 静态默认值后,清运行时配置缓存与插件加载缓存,
|
||||
// 使下次 readConfig() 回源重新合并(文件默认值 + 数据库已保存值),
|
||||
// 否则仅改了文件、DB/缓存未动,运行时 config('插件名') 仍读旧值。
|
||||
\ywxapp\service\AddonService::clearConfigCache($this->addon);
|
||||
\ywxapp\service\AppService::clearAddonCache();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1334,7 +1334,7 @@ class AddonService
|
||||
if ($config === null || !empty($changed)) {
|
||||
self::ensureConfigTable();
|
||||
// 从统一配置表读取「独立项」(addon + name 唯一,每行一个配置项)
|
||||
$rows = Db::name('addon_config')->where('addon', $name)->column('value', 'name');
|
||||
$rows = Db::name('common_addonconf')->where('addon', $name)->column('value', 'name');
|
||||
$config = $rows ?: [];
|
||||
// 平滑迁移:若该插件此前用 .addonrc 文件存过配置,则一次性导入数据库
|
||||
if (empty($config)) {
|
||||
@@ -1372,14 +1372,14 @@ class AddonService
|
||||
|
||||
/**
|
||||
* 自愈建表:确保统一插件配置表存在(CREATE TABLE IF NOT EXISTS)
|
||||
* 表结构:wxapp_addon_config,每个配置项独立成行(addon + name 唯一)
|
||||
* 表结构:{prefix}common_addonconf,每个配置项独立成行(addon + name 唯一)
|
||||
*/
|
||||
private static function ensureConfigTable(): void
|
||||
{
|
||||
try {
|
||||
$defaultDb = Config::get('database.default');
|
||||
$prefix = Config::get("database.connections.{$defaultDb}.prefix");
|
||||
$table = $prefix . 'addon_config';
|
||||
$table = $prefix . 'common_addonconf';
|
||||
$exists = Db::query("SHOW TABLES LIKE '{$table}'");
|
||||
if (empty($exists)) {
|
||||
Db::execute("CREATE TABLE IF NOT EXISTS `{$table}` (
|
||||
@@ -1407,7 +1407,7 @@ class AddonService
|
||||
{
|
||||
$defaultDb = Config::get('database.default');
|
||||
$prefix = Config::get("database.connections.{$defaultDb}.prefix");
|
||||
$table = $prefix . 'addon_config';
|
||||
$table = $prefix . 'common_addonconf';
|
||||
$time = time();
|
||||
foreach ($values as $key => $val) {
|
||||
$val = is_array($val) ? json_encode($val, JSON_UNESCAPED_UNICODE) : (string) $val;
|
||||
@@ -1508,7 +1508,7 @@ class AddonService
|
||||
public function upgrade(string $currentVersion = ''): bool
|
||||
{
|
||||
if ($currentVersion === '') {
|
||||
$installed = \ywxapp\model\AddonModel::where('name', $this->addon)->value('version');
|
||||
$installed = \ywxapp\model\CommonAddon::where('name', $this->addon)->value('version');
|
||||
$currentVersion = $installed ?: '';
|
||||
}
|
||||
$this->callAddonHook('upgrade', [$currentVersion]);
|
||||
@@ -1951,6 +1951,13 @@ class AddonService
|
||||
} catch (\PDOException $e) {
|
||||
Log::error("Addon {$operation} error (Database): " . $e->getMessage());
|
||||
throw new Exception(lang('数据库操作失败'));
|
||||
} catch (\Error $e) {
|
||||
Log::error("Addon {$operation} error (Error): " . $e->getMessage());
|
||||
// 开发环境透传原始错误信息便于定位;生产环境统一返回通用提示,避免泄露路径等内部细节
|
||||
if (Env::get('app_debug')) {
|
||||
throw new Exception('插件操作失败:' . $e->getMessage());
|
||||
}
|
||||
throw new Exception('插件操作失败,请稍后重试或联系管理员');
|
||||
} catch (\Exception $e) {
|
||||
Log::error("Addon {$operation} error: " . $e->getMessage());
|
||||
// 开发环境透传原始异常信息(如 Addon already exists / version incorrect)便于定位;
|
||||
@@ -1958,7 +1965,7 @@ class AddonService
|
||||
if (Env::get('app_debug')) {
|
||||
throw new Exception('插件操作失败:' . $e->getMessage());
|
||||
}
|
||||
throw new Exception('插件操作失败,请稍后重试或联系管理员' . $e->getMessage());
|
||||
throw new Exception('插件操作失败,请稍后重试或联系管理员');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2565,8 +2572,9 @@ class AddonService
|
||||
// 不存在(即未指定可用顶级菜单)则回退到「以插件名为标题的顶级菜单」。
|
||||
// 2) menu.json 未声明 superior:直接生成一个以插件名为标题的顶级菜单(pid=0),
|
||||
// 所有菜单都注入到这个插件名顶级菜单下。
|
||||
$superior = $menu['superior'] ?? '';
|
||||
if ($superior !== '') {
|
||||
$superior = $menu['parent'] ?? 'app';
|
||||
$isRoot = $menu['is_root'] ?? false;
|
||||
if ($isRoot== false) {
|
||||
$appPid = $this->getOrCreateAppMenuId($superior);
|
||||
if ($appPid > 0) {
|
||||
// 指定顶级菜单存在:在其下挂「插件名分组」,子项再挂该分组下
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @Author: YwxApp <ywx@ywxapp.cn>
|
||||
* @Date: 2026-08-06 22:05:48
|
||||
* @LastEditors: YwxApp <ywx@ywxapp.cn>
|
||||
* @LastEditTime: 2026-08-13 16:46:11
|
||||
* @LastEditTime: 2026-08-20 11:19:15
|
||||
* @Description:
|
||||
* @FilePath: \ywxapp_dev\ywxapp\service\AppService.php
|
||||
* @CustomString: Copyright (c) 2026 YwxApp
|
||||
@@ -74,7 +74,7 @@ class AppService extends Service
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
\think\facade\Config::set(\ywxapp\addon::readConfig($name), $name);
|
||||
\think\facade\Config::set(\ywxapp\AddonBase::readConfig($name), $name);
|
||||
} catch (\Throwable $e) {
|
||||
// 单个插件预热失败不影响主流程
|
||||
}
|
||||
@@ -82,23 +82,29 @@ class AppService extends Service
|
||||
});
|
||||
// 默认短信发送监听:开发环境不实际下发(仅记录验证码到日志并回传前端用于调试),
|
||||
// 生产环境返回 null,交由短信插件(监听 SmsSend 事件)实际发送。
|
||||
\think\facade\Event::listen('SmsSend', function ($sms) {
|
||||
if (Env::get('app_debug')) {
|
||||
\think\facade\Log::info('[DEV] 短信验证码(未实际下发): ' . ($sms['code'] ?? '') . ' -> ' . ($sms['mobile'] ?? ''));
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// \think\facade\Event::listen('SmsSend', function ($sms) {
|
||||
// if (Env::get('app_debug')) {
|
||||
// \think\facade\Log::info('[DEV] 短信验证码(未实际下发): ' . ($sms['code'] ?? '') . ' -> ' . ($sms['mobile'] ?? ''));
|
||||
// return true;
|
||||
// }
|
||||
// return null;
|
||||
// });
|
||||
}
|
||||
|
||||
|
||||
public function boot()
|
||||
{
|
||||
$this->app->middleware->import([MultiApp::class], 'global');
|
||||
|
||||
|
||||
// 2. 替换 url() 使用的 UrlBuild 类
|
||||
$this->app->bind(\think\route\Url::class, UrlBuild::class);
|
||||
$this->commands([
|
||||
// 插件命令自动注册约定:
|
||||
// 插件在 info.php 声明 'commands' => ['插件名:动作' => \addon\<插件名>\command\Xxx::class]
|
||||
// 命令类 extends think\console\Command,psr-4 (addon\ => addon) 自动加载。
|
||||
// 执行链:Console 构造 → app->initialize() → BootService 触发本 boot() →
|
||||
// $this->commands() 挂 Console::starting 回调 → Console::run() 统一 addCommands。
|
||||
// 坏类名/非 Command 子类会被 addCommands 跳过,单个插件声明错误不影响整体。
|
||||
$commands = [
|
||||
'app' => \ywxapp\command\AppCommand::class,
|
||||
'addon:manage' => \ywxapp\command\AddonManage::class,
|
||||
'addon:make' => \ywxapp\command\AddonMake::class,
|
||||
@@ -106,7 +112,14 @@ class AppService extends Service
|
||||
'addon:license-check' => \ywxapp\command\AddonLicenseCheck::class,
|
||||
'addon:repair' => \ywxapp\command\AddonRepair::class,
|
||||
'ywxapp:upgrade' => \ywxapp\command\Upgrade::class,
|
||||
]);
|
||||
];
|
||||
foreach ($this->getEnabledaddon() as $info) {
|
||||
$addonCmds = $info['commands'] ?? [];
|
||||
if (is_array($addonCmds) && $addonCmds) {
|
||||
$commands = array_merge($commands, $addonCmds);
|
||||
}
|
||||
}
|
||||
$this->commands($commands);
|
||||
$this->loadAddonRelevant();
|
||||
// $this->loadAddonRoutes();
|
||||
Route::rule('ueditor', function () {
|
||||
@@ -252,10 +265,18 @@ class AppService extends Service
|
||||
public static function clearAddonCache(): void
|
||||
{
|
||||
\think\facade\Cache::delete('addon_loaded_config');
|
||||
// 同时清除配置缓存
|
||||
$addon = \think\facade\Config::get('addon', []);
|
||||
foreach ($addon as $addon) {
|
||||
\think\facade\Cache::delete('addon_config_' . $addon['name']);
|
||||
// 同时清除各插件的配置缓存。
|
||||
// 注意:Config::get('addon') 返回的是框架 addon 组件自身配置(含 multi_app/priority 等),
|
||||
// 并非插件列表,切勿直接 foreach 它(遍历到 multi_app=true 会触发 "array offset on true")。
|
||||
// 正确的插件名来源是 addon 主表的 name 列。
|
||||
try {
|
||||
$names = \ywxapp\model\CommonAddon::column('name');
|
||||
foreach ($names as $name) {
|
||||
\think\facade\Cache::delete('addon_config_' . $name);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// 数据表可能尚未创建,忽略
|
||||
\think\facade\Log::warning('clearAddonCache 获取插件列表失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +291,7 @@ class AppService extends Service
|
||||
return true; // 无市场插件 → 无授权域,放行
|
||||
}
|
||||
try {
|
||||
$addonId = \ywxapp\model\AddonModel::where('name', $addon)->value('id');
|
||||
$addonId = \ywxapp\model\CommonAddon::where('name', $addon)->value('id');
|
||||
if (!$addonId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ class WalletService
|
||||
*/
|
||||
public static function ensure(int $uid): void
|
||||
{
|
||||
$exist = Db::name('member_wallets')->where('uid', $uid)->value('id');
|
||||
$exist = Db::name('member_wallet')->where('uid', $uid)->value('id');
|
||||
if (! $exist) {
|
||||
Db::name('member_wallets')->insert([
|
||||
Db::name('member_wallet')->insert([
|
||||
'uid' => $uid,
|
||||
'balance' => 0,
|
||||
'coins' => 0,
|
||||
@@ -43,7 +43,7 @@ class WalletService
|
||||
public static function getCoins(int $uid): int
|
||||
{
|
||||
self::ensure($uid);
|
||||
return (int) Db::name('member_wallets')->where('uid', $uid)->value('coins');
|
||||
return (int) Db::name('member_wallet')->where('uid', $uid)->value('coins');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,7 +57,7 @@ class WalletService
|
||||
self::ensure($uid);
|
||||
Db::startTrans();
|
||||
try {
|
||||
Db::name('member_wallets')->where('uid', $uid)->inc('coins', $num)->update(['update_at' => time()]);
|
||||
Db::name('member_wallet')->where('uid', $uid)->inc('coins', $num)->update(['update_at' => time()]);
|
||||
Db::name('member_bill')->insert([
|
||||
'uid' => $uid,
|
||||
'type' => 1,
|
||||
@@ -85,13 +85,13 @@ class WalletService
|
||||
return true;
|
||||
}
|
||||
self::ensure($uid);
|
||||
$cur = Db::name('member_wallets')->where('uid', $uid)->lock(true)->value('coins');
|
||||
$cur = Db::name('member_wallet')->where('uid', $uid)->lock(true)->value('coins');
|
||||
if ($cur < $num) {
|
||||
throw new \Exception('金币不足');
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
Db::name('member_wallets')->where('uid', $uid)
|
||||
Db::name('member_wallet')->where('uid', $uid)
|
||||
->dec('coins', $num)
|
||||
->inc('total_consume', $num)
|
||||
->update(['update_at' => time()]);
|
||||
|
||||
Reference in New Issue
Block a user