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:
@@ -372,15 +372,21 @@ class AddonHealth extends Command
|
||||
|
||||
Cache::delete($testKey);
|
||||
|
||||
// 插件缓存命中情况(仅警告,不影响健康)
|
||||
$addon = Config::get('addon', []);
|
||||
foreach ($addon as $addon) {
|
||||
if (!isset($addon['name']) || !($addon['state'] ?? false)) {
|
||||
continue;
|
||||
}
|
||||
if (Cache::get('addon_config_' . $addon['name']) === false) {
|
||||
$result['warnings'][] = "插件 {$addon['name']} 缓存未命中,可能影响性能";
|
||||
// 插件缓存命中情况(仅警告,不影响健康)。
|
||||
// 注意:Config::get('addon') 是框架 addon 组件配置(multi_app/priority 等),非插件列表;
|
||||
// 直接用 AddonModel 的插件名列表,避免遍历配置时访问 true['name']。
|
||||
try {
|
||||
$addons = \ywxapp\model\AddonModel::field('name,state')->select()->toArray();
|
||||
foreach ($addons as $addon) {
|
||||
if (!isset($addon['name']) || !($addon['state'] ?? false)) {
|
||||
continue;
|
||||
}
|
||||
if (Cache::get('addon_config_' . $addon['name']) === false) {
|
||||
$result['warnings'][] = "插件 {$addon['name']} 缓存未命中,可能影响性能";
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// 数据表可能尚未创建,忽略
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$result['healthy'] = false;
|
||||
|
||||
@@ -53,7 +53,7 @@ class AddonMake extends Make
|
||||
$this->setName('addon:make')
|
||||
->addArgument('addon', Argument::OPTIONAL, 'addon name, e.g. demo (required for build & generators)')
|
||||
->addOption('action', 'a', Option::VALUE_REQUIRED, 'build|clear|controller|model|middleware|validate|event|listener|service|subscribe')
|
||||
->addOption('name', 'n', Option::VALUE_OPTIONAL, 'class name for generator actions, e.g. User')
|
||||
->addOption('name', null, Option::VALUE_OPTIONAL, 'class name for generator actions, e.g. User')
|
||||
->addOption('api', null, Option::VALUE_NONE, 'controller: generate an api controller class.')
|
||||
->addOption('plain', null, Option::VALUE_NONE, 'controller: generate an empty controller class.')
|
||||
->addOption('cache', 'c', Option::VALUE_NONE, 'clear: clear cache file')
|
||||
@@ -62,7 +62,7 @@ class AddonMake extends Make
|
||||
->setDescription('Unified addon scaffold & class generator (build / clear / controller / model / middleware / validate / event / listener / service / subscribe)');
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected function execute(Input $input, Output $output): int
|
||||
{
|
||||
$addon = trim((string) $input->getArgument('addon'));
|
||||
@@ -227,8 +227,12 @@ class AddonMake extends Make
|
||||
mkdir($appPath . 'controller');
|
||||
mkdir($appPath . 'controller/backend');
|
||||
mkdir($appPath . 'controller/member');
|
||||
mkdir($appPath . 'controller/api');
|
||||
}
|
||||
$this->buildIndexController($app, $namespace);
|
||||
$this->buildIndexController($app, $namespace); // 前台 FrontendBase
|
||||
$this->buildIndexController($app, $namespace, 'backend'); // BackendBase
|
||||
$this->buildIndexController($app, $namespace, 'member'); // MemberBase
|
||||
$this->buildIndexController($app, $namespace, 'api'); // ApiBase
|
||||
if (! is_dir($appPath . 'lang')) {
|
||||
mkdir($appPath . 'lang');
|
||||
}
|
||||
@@ -342,14 +346,25 @@ class AddonMake extends Make
|
||||
* @param string $namespace 类库命名空间
|
||||
* @return void
|
||||
*/
|
||||
public function buildIndexController(string $app, string $namespace): void
|
||||
public function buildIndexController(string $app, string $namespace, string $area = ''): void
|
||||
{
|
||||
$suffix = $this->app->config->get('route.controller_suffix') ? 'Controller' : '';
|
||||
$filename = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '') . 'controller' . DIRECTORY_SEPARATOR . 'Index' . $suffix . '.php';
|
||||
|
||||
$dir = 'controller' . ($area !== '' ? DIRECTORY_SEPARATOR . $area : '');
|
||||
$filename = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '') . $dir . DIRECTORY_SEPARATOR . 'Index' . $suffix . '.php';
|
||||
if (! is_file($filename)) {
|
||||
$content = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'addon' . DIRECTORY_SEPARATOR . 'controller.stub');
|
||||
$content = str_replace(['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%app_namespace%}'], ['Index', $suffix, $namespace . '\\controller', $namespace], $content);
|
||||
$base_class = $area == 'backend' ? 'BackendBase' : ($area == 'member' ? 'MemberBase' : ($area == 'api' ? 'ApiBase' : 'FrontendBase'));
|
||||
$content = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'addon' . DIRECTORY_SEPARATOR . 'controller.stub');
|
||||
$content = str_replace(
|
||||
[
|
||||
'{%className%}',
|
||||
'{%actionSuffix%}',
|
||||
'{%namespace%}',
|
||||
'{%app_namespace%}',
|
||||
'{%baseClass%}'
|
||||
],
|
||||
['Index', $suffix, $namespace . ($area !== '' ? '\\controller\\' . $area : '\\controller'), $namespace, $base_class],
|
||||
$content
|
||||
);
|
||||
$this->checkDirBuild(dirname($filename));
|
||||
file_put_contents($filename, $content);
|
||||
}
|
||||
@@ -366,8 +381,16 @@ class AddonMake extends Make
|
||||
{
|
||||
$filename = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '') . 'route' . DIRECTORY_SEPARATOR . 'app.php';
|
||||
if (! is_file($filename)) {
|
||||
$content = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'addon' . DIRECTORY_SEPARATOR . 'route.stub');
|
||||
$content = str_replace(
|
||||
[
|
||||
'{%app%}'
|
||||
],
|
||||
[$app],
|
||||
$content
|
||||
);
|
||||
$this->checkDirBuild(dirname($filename));
|
||||
file_put_contents($filename, "<?php" . PHP_EOL . "use think\\facade\\Route;" . PHP_EOL . PHP_EOL);
|
||||
file_put_contents($filename, $content);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ use think\File;
|
||||
use ywxapp\exception\AddonException;
|
||||
use ywxapp\service\AddonService;
|
||||
use ywxapp\service\AddonHotReload;
|
||||
use think\facade\Env;
|
||||
|
||||
/**
|
||||
* 插件统一管理命令(合并 develop / install / reload)
|
||||
@@ -94,8 +95,14 @@ class AddonManage extends Command
|
||||
} catch (AddonException $e) {
|
||||
$output->writeln('<error>' . $e->getMessage() . '</error>');
|
||||
return 1;
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
// \Throwable 兜底:\Error(如类型错误)不是 \Exception 子类,必须单独捕获,
|
||||
// 否则会漏到框架层导致无友好提示。
|
||||
$output->writeln('<error>开发模式安装失败:' . $e->getMessage() . '</error>');
|
||||
if (Env::get('app_debug')) {
|
||||
$output->writeln('<error>FILE: ' . $e->getFile() . ':' . $e->getLine() . '</error>');
|
||||
$output->writeln('<error>TRACE: ' . $e->getTraceAsString() . '</error>');
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -26,7 +26,7 @@ use ywxapp\model\BaseModel;
|
||||
* 整合以下自愈能力,使一个出问题的插件恢复到「菜单正确 + 表结构完整 + 配置齐全」的状态:
|
||||
* 1. 重载菜单:refreshMenu()(清旧 admin_power/user_rule + 重建,顺带修复菜单路由前缀)
|
||||
* 2. 数据库自愈:解析 install.sql 中该插件全部 CREATE TABLE,缺表建表、缺列补列
|
||||
* 3. 配置行补全:确保 addon_config 表存在该插件配置行(无则按默认 info 写入)
|
||||
* 3. 配置行补全:确保 common_addonconf 表存在该插件配置行(无则按默认 info 写入)
|
||||
*
|
||||
* 用法:
|
||||
* php think addon:repair <插件名> # 修复指定插件(菜单 + 表 + 配置)
|
||||
@@ -245,24 +245,25 @@ class AddonRepair extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置行补全:确保 addon_config 表有该插件配置行。
|
||||
* 配置行补全:确保 common_addonconf 表有该插件配置行。
|
||||
*/
|
||||
private function repairConfig(AddonService $svc, string $name, Output $output): bool
|
||||
{
|
||||
$default = Config::get('database.default');
|
||||
$prefix = Config::get("database.connections.{$default}.prefix") ?: '';
|
||||
$configTable = $prefix . 'addon_config';
|
||||
$configTable = $prefix . 'common_addonconf';
|
||||
try {
|
||||
if (!BaseModel::tableExists($configTable)) {
|
||||
$output->writeln('<comment>[配置] addon_config 表不存在,跳过</comment>');
|
||||
$output->writeln('<comment>[配置] common_addonconf 表不存在,跳过</comment>');
|
||||
return true;
|
||||
}
|
||||
$row = Db::table($configTable)->where('name', $name)->find();
|
||||
if (!empty($row)) {
|
||||
// common_addonconf 设计:每行一个配置项(addon + name 唯一),故按 addon 判断是否存在
|
||||
$existing = Db::table($configTable)->where('addon', $name)->count();
|
||||
if ($existing > 0) {
|
||||
$output->writeln('<comment>[配置] 配置行已存在,跳过</comment>');
|
||||
return true;
|
||||
}
|
||||
// 读取插件默认配置(config.php 返回数组)
|
||||
// 读取插件默认配置(config.php 返回关联数组 name=>value)
|
||||
$configFile = $this->addonDir($name) . 'config.php';
|
||||
$value = [];
|
||||
if (is_file($configFile)) {
|
||||
@@ -271,10 +272,13 @@ class AddonRepair extends Command
|
||||
$value = $cfg;
|
||||
}
|
||||
}
|
||||
$data = [
|
||||
'name' => $name,
|
||||
'value' => json_encode($value, JSON_UNESCAPED_UNICODE),
|
||||
];
|
||||
if (empty($value)) {
|
||||
$output->writeln('<comment>[配置] 无默认配置,跳过</comment>');
|
||||
return true;
|
||||
}
|
||||
// 逐配置项写入独立行(与 AddonService::config/$changed 的存储格式一致)
|
||||
\ywxapp\service\AddonService::config($name, $value);
|
||||
$data = []; // 已通过 AddonService::config 写入,无需再 insert
|
||||
// 兼容不同时间戳字段名(createtime/updatetime 或 create_time/update_time)
|
||||
$cols = Db::query("SHOW COLUMNS FROM `{$configTable}`");
|
||||
$colNames = array_column($cols, 'Field');
|
||||
|
||||
+70
-253
@@ -17,6 +17,8 @@ use think\console\Output;
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Option;
|
||||
|
||||
use think\console\command\Make;
|
||||
|
||||
/**
|
||||
* AppCommand 应用命令统一入口
|
||||
*
|
||||
@@ -44,25 +46,8 @@ use think\console\input\Option;
|
||||
* php think app clear --cache # 清理缓存
|
||||
* php think app build myapp # 构建应用目录
|
||||
*/
|
||||
class AppCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Make 型子命令配置
|
||||
* type : 类型标签(用于成功/存在提示)
|
||||
* stub : stub 文件名(不含 .stub 扩展)
|
||||
* ns : 命名空间末尾段(如 controller/model/event ...)
|
||||
* suffix : 类名后缀(仅 controller 在开启 controller_suffix 时追加 Controller)
|
||||
*/
|
||||
private const MAKE_TYPES = [
|
||||
'controller' => ['type' => 'Controller', 'stub' => 'controller', 'ns' => 'controller', 'suffix' => 'Controller'],
|
||||
'model' => ['type' => 'Model', 'stub' => 'model', 'ns' => 'model', 'suffix' => ''],
|
||||
'event' => ['type' => 'Event', 'stub' => 'event', 'ns' => 'event', 'suffix' => ''],
|
||||
'listener' => ['type' => 'Listener', 'stub' => 'listener', 'ns' => 'listener', 'suffix' => ''],
|
||||
'middleware' => ['type' => 'Middleware', 'stub' => 'middleware', 'ns' => 'middleware', 'suffix' => ''],
|
||||
'service' => ['type' => 'Service', 'stub' => 'service', 'ns' => 'service', 'suffix' => ''],
|
||||
'subscribe' => ['type' => 'Subscribe', 'stub' => 'subscribe', 'ns' => 'subscribe', 'suffix' => ''],
|
||||
'validate' => ['type' => 'Validate', 'stub' => 'validate', 'ns' => 'validate', 'suffix' => ''],
|
||||
];
|
||||
class AppCommand extends Make
|
||||
{
|
||||
|
||||
/**
|
||||
* 应用基础目录(Build 使用)
|
||||
@@ -70,12 +55,43 @@ class AppCommand extends Command
|
||||
*/
|
||||
private $basePath = '';
|
||||
|
||||
/**
|
||||
* stub 模板目录(已从 ywxapp/command/app/stubs 上移至此)
|
||||
*/
|
||||
private function stubDir(): string
|
||||
private $subCommon = '';
|
||||
|
||||
protected function getStub(): string
|
||||
{
|
||||
return __DIR__ . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR;
|
||||
if ($this->input->getOption('api')) {
|
||||
return __DIR__ . DIRECTORY_SEPARATOR . 'appstubs' . DIRECTORY_SEPARATOR . $this->subCommon . '.api.stub';
|
||||
}
|
||||
if ($this->input->getOption('plain')) {
|
||||
return __DIR__ . DIRECTORY_SEPARATOR . 'appstubs' . DIRECTORY_SEPARATOR . $this->subCommon . '.plain.stub';
|
||||
}
|
||||
return __DIR__ . DIRECTORY_SEPARATOR . 'appstubs' . DIRECTORY_SEPARATOR . $this->subCommon . '.stub';
|
||||
}
|
||||
|
||||
protected function getNamespace(string $app): string
|
||||
{
|
||||
if ($this->subCommon === 'model') {
|
||||
return 'ywxapp\\model';
|
||||
}
|
||||
return parent::getNamespace($app) . '\\' . $this->subCommon;
|
||||
}
|
||||
|
||||
protected function getClassName(string $name): string
|
||||
{
|
||||
if ($this->subCommon == 'controller') {
|
||||
return parent::getClassName($name) . ($this->app->config->get('route.controller_suffix') ? 'Controller' : '');
|
||||
}
|
||||
return parent::getClassName($name);
|
||||
}
|
||||
|
||||
protected function getPathName(string $name): string
|
||||
{
|
||||
if ($this->subCommon == 'model') {
|
||||
$name = substr($name, 6);
|
||||
return $this->app->getRootPath() . 'ywxapp' . '/' . ltrim(str_replace('\\', '/', $name), '/') . '.php';
|
||||
}
|
||||
$name = substr($name, 4);
|
||||
return $this->app->getBasePath() . ltrim(str_replace('\\', '/', $name), '/') . '.php';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,171 +114,42 @@ class AppCommand extends Command
|
||||
/**
|
||||
* 执行命令
|
||||
*/
|
||||
protected function execute(Input $input, Output $output): int
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
if ($input->getOption('list') || empty($input->getArgument('subcommand'))) {
|
||||
$this->showList($output);
|
||||
return 0;
|
||||
}
|
||||
|
||||
$sub = $input->getArgument('subcommand');
|
||||
$name = (string) $input->getArgument('name');
|
||||
|
||||
if (isset(self::MAKE_TYPES[$sub])) {
|
||||
return $this->runMake($sub, $name, $input, $output);
|
||||
}
|
||||
if ($sub === 'build') {
|
||||
$this->subCommon = $input->getArgument('subcommand');
|
||||
$name = trim($input->getArgument('name'));
|
||||
if ($this->subCommon === 'build') {
|
||||
return $this->runBuild($name, $output);
|
||||
}
|
||||
if ($sub === 'clear') {
|
||||
if ($this->subCommon === 'clear') {
|
||||
return $this->runClear($name, $input, $output);
|
||||
}
|
||||
|
||||
$output->writeln('<error>未知子命令: ' . $sub . '</error>');
|
||||
$this->showList($output);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出所有可用子命令
|
||||
*/
|
||||
private function showList(Output $output): void
|
||||
{
|
||||
$output->writeln('<info>YwxApp 应用命令(`php think app <子命令> [参数]`):</info>');
|
||||
$output->writeln('');
|
||||
$width = max(array_map('strlen', array_keys(self::MAKE_TYPES + ['build' => 1, 'clear' => 1])));
|
||||
foreach (array_keys(['build' => 1, 'clear' => 1] + self::MAKE_TYPES) as $name) {
|
||||
$desc = [
|
||||
'build' => '构建应用目录',
|
||||
'clear' => '清理运行时文件',
|
||||
'controller' => '生成控制器(--api/--plain)',
|
||||
'model' => '生成模型',
|
||||
'event' => '生成事件',
|
||||
'listener' => '生成监听器',
|
||||
'middleware' => '生成中间件',
|
||||
'service' => '生成服务',
|
||||
'subscribe' => '生成订阅器',
|
||||
'validate' => '生成验证器',
|
||||
][$name];
|
||||
$output->writeln(' ' . str_pad($name, $width) . ' ' . $desc);
|
||||
}
|
||||
$output->writeln('');
|
||||
$output->writeln('示例:php think app controller app\\index');
|
||||
$output->writeln(' php think app clear --cache');
|
||||
}
|
||||
|
||||
/* ===================== Make 型生成器 ===================== */
|
||||
|
||||
/**
|
||||
* 运行 Make 型子命令
|
||||
*/
|
||||
private function runMake(string $type, string $name, Input $input, Output $output): int
|
||||
{
|
||||
if ($name === '') {
|
||||
$output->writeln('<error>缺少类名参数,用法:php think app ' . $type . ' <类名></error>');
|
||||
return 1;
|
||||
}
|
||||
|
||||
$cfg = self::MAKE_TYPES[$type];
|
||||
$classname = $this->makeClassName($type, $name);
|
||||
$pathname = $this->makePathName($classname);
|
||||
|
||||
$classname = $this->getClassName($name);
|
||||
$pathname = $this->getPathName($classname);
|
||||
if (is_file($pathname)) {
|
||||
$output->writeln('<error>' . $cfg['type'] . ':' . $classname . ' already exists!</error>');
|
||||
return 1;
|
||||
$output->writeln('<error>' . $this->type . ':' . $classname . ' already exists!</error>');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_dir(dirname($pathname))) {
|
||||
mkdir(dirname($pathname), 0755, true);
|
||||
}
|
||||
|
||||
file_put_contents($pathname, $this->buildClass($classname, $type, $input));
|
||||
|
||||
$output->writeln('<info>' . $cfg['type'] . ':' . $classname . ' created successfully.</info>');
|
||||
return 0;
|
||||
file_put_contents($pathname, $this->buildClass($classname));
|
||||
$output->writeln('<info>' . $this->type . ':' . $classname . ' created successfully.</info>');
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 stub 路径(controller 支持 --api/--plain)
|
||||
*/
|
||||
private function makeStub(string $type, Input $input): string
|
||||
protected function buildClass(string $name)
|
||||
{
|
||||
if ($type === 'controller') {
|
||||
if ($input->getOption('api')) {
|
||||
return $this->stubDir() . 'controller.api.stub';
|
||||
}
|
||||
if ($input->getOption('plain')) {
|
||||
return $this->stubDir() . 'controller.plain.stub';
|
||||
}
|
||||
return $this->stubDir() . 'controller.stub';
|
||||
}
|
||||
|
||||
return $this->stubDir() . self::MAKE_TYPES[$type]['stub'] . '.stub';
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析命名空间
|
||||
*/
|
||||
private function makeNamespace(string $type, string $app): string
|
||||
{
|
||||
return 'app' . ($app ? '\\' . $app : '') . '\\' . self::MAKE_TYPES[$type]['ns'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析完整类名(等价于 think\console\command\Make::getClassName + Controller 后缀)
|
||||
*/
|
||||
private function makeClassName(string $type, string $name): string
|
||||
{
|
||||
if (str_contains($name, '\\')) {
|
||||
$class = $name;
|
||||
} else {
|
||||
$app = '';
|
||||
if (str_contains($name, '@')) {
|
||||
[$app, $name] = explode('@', $name);
|
||||
}
|
||||
if (str_contains($name, '/')) {
|
||||
$name = str_replace('/', '\\', $name);
|
||||
}
|
||||
$class = $this->makeNamespace($type, $app) . '\\' . $name;
|
||||
}
|
||||
|
||||
$suffix = self::MAKE_TYPES[$type]['suffix'];
|
||||
if ($suffix !== '' && $this->app->config->get('route.controller_suffix')) {
|
||||
$class .= $suffix;
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析文件路径(等价于各 Make 命令重写的 getPathName)
|
||||
*/
|
||||
private function makePathName(string $name): string
|
||||
{
|
||||
return $this->app->getRootPath() . ltrim(str_replace('\\', '/', $name), '/') . '.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* 用 stub 渲染类文件(等价于 think\console\command\Make::buildClass)
|
||||
*/
|
||||
private function buildClass(string $name, string $type, Input $input): string
|
||||
{
|
||||
$stub = file_get_contents($this->makeStub($type, $input));
|
||||
|
||||
$stub = file_get_contents($this->getStub());
|
||||
$namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
|
||||
$class = str_replace($namespace . '\\', '', $name);
|
||||
|
||||
return str_replace(
|
||||
['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%app_namespace%}'],
|
||||
[
|
||||
$class,
|
||||
$this->app->config->get('route.action_suffix'),
|
||||
$namespace,
|
||||
$this->app->getNamespace(),
|
||||
],
|
||||
$stub
|
||||
);
|
||||
$class = str_replace($namespace . '\\', '', $name);
|
||||
return str_replace(['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%app_namespace%}'], [
|
||||
$class,
|
||||
$this->app->config->get('route.action_suffix'),
|
||||
$namespace,
|
||||
$this->app->getNamespace(),
|
||||
], $stub);
|
||||
}
|
||||
|
||||
|
||||
/* ===================== Build 命令 ===================== */
|
||||
|
||||
@@ -273,18 +160,20 @@ class AppCommand extends Command
|
||||
{
|
||||
$this->basePath = $this->app->getRootPath() . 'app' . DIRECTORY_SEPARATOR;
|
||||
$app = $app ?: '';
|
||||
|
||||
if (is_file($this->basePath . 'build.php')) {
|
||||
$list = include $this->basePath . 'build.php';
|
||||
} else {
|
||||
$list = [
|
||||
'__dir__' => ['controller', 'model', 'view'],
|
||||
'__file__' => ['common.php', 'event.php', 'middleware.php', 'service.php'],
|
||||
'__dir__' => ['controller', 'model', 'view', 'route'],
|
||||
'controller' => ['Index'],
|
||||
'model' => [],
|
||||
'view' => ['index/index'],
|
||||
'route' => ['app'],
|
||||
];
|
||||
}
|
||||
|
||||
$this->buildApp($app, $list);
|
||||
$output->writeln('<info>Successed</info>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -296,15 +185,8 @@ class AppCommand extends Command
|
||||
if (!is_dir($this->basePath . $app)) {
|
||||
mkdir($this->basePath . $app);
|
||||
}
|
||||
|
||||
$appPath = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '');
|
||||
$namespace = 'app' . ($app ? '\\' . $app : '');
|
||||
|
||||
$this->buildCommon($app);
|
||||
$this->buildHello($app, $namespace);
|
||||
$this->buildapp1($app, $namespace);
|
||||
$this->buildInfo($app);
|
||||
|
||||
foreach ($list as $path => $file) {
|
||||
if ('__dir__' == $path) {
|
||||
foreach ($file as $dir) {
|
||||
@@ -330,6 +212,9 @@ class AppCommand extends Command
|
||||
}
|
||||
$content = '<?php' . PHP_EOL . 'namespace ' . $space . ';' . PHP_EOL . PHP_EOL . 'class ' . $class . PHP_EOL . '{' . PHP_EOL . PHP_EOL . '}';
|
||||
break;
|
||||
case 'route':
|
||||
$content = "<?php" . PHP_EOL . PHP_EOL . "use think\\facade\Route;";
|
||||
break;
|
||||
case 'model':
|
||||
$content = '<?php' . PHP_EOL . 'namespace ' . $space . ';' . PHP_EOL . PHP_EOL . 'use think\Model;' . PHP_EOL . PHP_EOL . 'class ' . $class . ' extends Model' . PHP_EOL . '{' . PHP_EOL . PHP_EOL . '}';
|
||||
break;
|
||||
@@ -350,70 +235,7 @@ class AppCommand extends Command
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建应用的欢迎页面
|
||||
*/
|
||||
private function buildHello(string $app, string $namespace): void
|
||||
{
|
||||
$suffix = $this->app->config->get('route.controller_suffix') ? 'Controller' : '';
|
||||
$filename = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '') . 'controller' . DIRECTORY_SEPARATOR . 'Index' . $suffix . '.php';
|
||||
|
||||
if (!is_file($filename)) {
|
||||
$content = file_get_contents($this->stubDir() . 'controller.stub');
|
||||
$content = str_replace(
|
||||
['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%app_namespace%}'],
|
||||
['Index', $suffix, $namespace . '\\controller', $this->app->getNamespace()],
|
||||
$content
|
||||
);
|
||||
$this->checkDirBuild(dirname($filename));
|
||||
file_put_contents($filename, $content);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildapp1(string $app, string $namespace): void
|
||||
{
|
||||
$appPath = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '');
|
||||
if (!is_file($appPath . 'app.php')) {
|
||||
$content = file_get_contents($this->stubDir() . 'app.stub');
|
||||
$content = str_replace(['{%namespace%}', '{%app%}', '{%className%}'], [$namespace, $app, 'app'], $content);
|
||||
file_put_contents($appPath . 'app.php', $content);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildInfo(string $app): void
|
||||
{
|
||||
$appPath = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '');
|
||||
if (!is_file($appPath . 'info.php')) {
|
||||
$array = [
|
||||
'name' => '',
|
||||
'title' => '',
|
||||
'intro' => '',
|
||||
'author' => '',
|
||||
'website' => '',
|
||||
'version' => '1.0.0',
|
||||
'state' => 1,
|
||||
'url' => '',
|
||||
'license' => '',
|
||||
'licenseto' => 0,
|
||||
];
|
||||
$array = array_merge($array, ['name' => $app, 'url' => '/' . $app]);
|
||||
$output = "<?php\n return " . var_export($array, true) . ";\n?>";
|
||||
file_put_contents($appPath . 'info.php', $output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建应用的公共文件
|
||||
*/
|
||||
private function buildCommon(string $app): void
|
||||
{
|
||||
$appPath = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '');
|
||||
foreach (['event', 'middleware', 'common', 'config'] as $nm) {
|
||||
if (!is_file($appPath . $nm . '.php')) {
|
||||
file_put_contents($appPath . $nm . '.php', '<?php' . PHP_EOL . '// 这是系统自动生成的' . $nm . '定义文件' . PHP_EOL . 'return [' . PHP_EOL . PHP_EOL . '];' . PHP_EOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建目录
|
||||
@@ -433,7 +255,6 @@ class AppCommand extends Command
|
||||
private function runClear(string $app, Input $input, Output $output): int
|
||||
{
|
||||
$runtimePath = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . ($app ? $app . DIRECTORY_SEPARATOR : '');
|
||||
|
||||
if ($input->getOption('cache')) {
|
||||
$path = $runtimePath . 'cache';
|
||||
} elseif ($input->getOption('log')) {
|
||||
@@ -441,12 +262,9 @@ class AppCommand extends Command
|
||||
} else {
|
||||
$path = $runtimePath;
|
||||
}
|
||||
|
||||
$rmdir = $input->getOption('dir') ? true : false;
|
||||
$this->clearRuntime(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR, $rmdir);
|
||||
|
||||
$output->writeln('<info>Clear Successed</info>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -456,7 +274,6 @@ class AppCommand extends Command
|
||||
private function clearRuntime(string $path, bool $rmdir): void
|
||||
{
|
||||
$files = is_dir($path) ? scandir($path) : [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ('.' != $file && '..' != $file && is_dir($path . $file)) {
|
||||
array_map('unlink', glob($path . $file . DIRECTORY_SEPARATOR . '*.*'));
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace {%namespace%};
|
||||
|
||||
use think\Request;
|
||||
|
||||
class {%className%} extends \ywxapp\controller\AddonFrontend
|
||||
class {%className%} extends \ywxapp\controller\{%baseClass%}
|
||||
{
|
||||
/**
|
||||
* Summary of needLogin
|
||||
@@ -33,7 +33,7 @@ class {%className%} extends \ywxapp\controller\AddonFrontend
|
||||
*/
|
||||
public function index{%actionSuffix%}()
|
||||
{
|
||||
return "这是一个{%app_namespace%} 插件应用";
|
||||
return "这是一个{%app_namespace%} 插件应用控制器{%namespace%}";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +74,7 @@ class {%className%} extends \ywxapp\controller\AddonFrontend
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function edit{%actionSuffix%}($id)
|
||||
public function edit{%actionSuffix%}($id = null)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use think\facade\Route;
|
||||
|
||||
|
||||
// 公共前缀 /{%app%},同时设置默认命名空间为 addon\{%app%}\controller
|
||||
|
||||
// 后台 → addon\{%app%}\controller\backend\Index
|
||||
Route::group('backend', function () {
|
||||
Route::get('index/index', 'index/index')->name('{%app%}_backend_index');
|
||||
})->namespace('addon\{%app%}\controller\backend');
|
||||
|
||||
// 用户中心 → addon\{%app%}\controller\member\Index
|
||||
Route::group('member', function () {
|
||||
Route::get('index/index', 'index/index')->name('{%app%}_member_index');
|
||||
})->namespace('addon\{%app%}\controller\member');
|
||||
|
||||
// API → apaddonp\{%app%}\controller\api\Index
|
||||
Route::group('api', function () {
|
||||
Route::get('index/index', 'index/index')->name('{%app%}_api_index');
|
||||
})->namespace('addon\{%app%}\controller\api');
|
||||
|
||||
// 前台 → addon\{%app%}\controller\Index(不加子分组)
|
||||
Route::get('index/index', 'index/index')->name('{%app%}_index');
|
||||
Reference in New Issue
Block a user