瀏覽代碼

refactor: 移除扩展脚手架命令并重构定时任务日志记录

runphp 1 周之前
父節點
當前提交
61825b3892
共有 2 個文件被更改,包括 1 次插入562 次删除
  1. 0 557
      src/Command/ExtensionScaffoldMakeCommand.php
  2. 1 5
      src/Hook/CronJobHook.php

+ 0 - 557
src/Command/ExtensionScaffoldMakeCommand.php

@@ -1,557 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace SixShop\System\Command;
-
-use think\console\Command;
-use think\console\Input;
-use think\console\input\Argument;
-use think\console\input\Option;
-use think\console\Output;
-
-use function SixShop\Core\extension_path;
-
-class ExtensionScaffoldMakeCommand extends Command
-{
-    protected function configure(): void
-    {
-        $this->setName('extension:make')
-            ->setDescription('生成扩展脚手架骨架(后端+前端,可选 Service/Entity/FFI/Frontend)')
-            ->addArgument('module', Argument::REQUIRED, '扩展模块名(目录名,建议小写下划线)')
-            ->addOption('with-api', null, Option::VALUE_NONE, '生成 API 路由与控制器')
-            ->addOption('with-admin', null, Option::VALUE_NONE, '生成 Admin 路由与控制器')
-            ->addOption('with-service', null, Option::VALUE_NONE, '生成 Service 层')
-            ->addOption('with-entity', null, Option::VALUE_NONE, '生成 Entity 层')
-            ->addOption('with-migration', null, Option::VALUE_NONE, '生成迁移与安装/卸载 SQL 样板')
-            ->addOption('with-frontend', null, Option::VALUE_NONE, '生成前端 Admin 模板')
-            ->addOption('with-ffi', null, Option::VALUE_NONE, '生成 FFI 目录与构建脚本样板')
-            ->addOption('adapter', null, Option::VALUE_REQUIRED, '服务适配默认实现 php|ffi|auto', 'php')
-            ->addOption('desc', null, Option::VALUE_REQUIRED, 'info.php 描述', '')
-            ->addOption('author', null, Option::VALUE_REQUIRED, '作者', 'yourname')
-            ->addOption('dry-run', null, Option::VALUE_NONE, '仅预览将要创建的目录与文件,不实际写入')
-            ->addOption('force', null, Option::VALUE_NONE, '允许在已存在的模块目录内覆盖写入文件');
-    }
-
-    protected function execute(Input $input, Output $output): int
-    {
-        $module = (string)$input->getArgument('module');
-        $withApi = (bool)$input->getOption('with-api');
-        $withAdmin = (bool)$input->getOption('with-admin');
-        $withService = (bool)$input->getOption('with-service');
-        $withEntity = (bool)$input->getOption('with-entity');
-        $withMigration = (bool)$input->getOption('with-migration');
-        $withFrontend = (bool)$input->getOption('with-frontend');
-        $withFFI = (bool)$input->getOption('with-ffi');
-        $adapter = (string)$input->getOption('adapter');
-        $desc = (string)$input->getOption('desc');
-        $author = (string)$input->getOption('author');
-        $dryRun = (bool)$input->getOption('dry-run');
-        $force = (bool)$input->getOption('force');
-
-        if (!$module) {
-            $output->error('模块名不能为空');
-            return 1;
-        }
-
-        // 默认行为:如果用户未显式指定任何 with-* 选项,则默认生成“完整插件”(除 FFI)
-        $anySpecified = $withApi || $withAdmin || $withService || $withEntity || $withMigration || $withFrontend || $withFFI;
-        if (!$anySpecified) {
-            $withApi = $withAdmin = $withService = $withEntity = $withMigration = $withFrontend = true;
-            // $withFFI 默认为 false,避免环境未开启 FFI 导致构建失败
-        }
-
-        $base = rtrim(extension_path($module), '/');
-        if (is_dir($base) && !$force) {
-            $output->error("扩展 {$module} 已存在:{$base},可使用 --force 覆盖写入");
-            return 1;
-        }
-
-        // 目录结构
-        $dirs = [
-            "$base/src/Controller/Api",
-            "$base/src/Controller/Admin",
-            "$base/src/Service",
-            "$base/src/Entity",
-            "$base/src/Hook",
-            "$base/database/migrations",
-            "$base/database/seeds",
-            "$base/route",
-            "$base/config",
-        ];
-        // 计划文件(用于 dry-run 展示)
-        $ns = "SixShop\\\\Extension\\\\{$module}";
-        $studly = str_replace(['-', '_'], '', ucwords($module, '-_'));
-        $planFiles = [
-            "$base/info.php",
-            "$base/config.php",
-            "$base/README.md",
-            "$base/src/Extension.php",
-            "$base/src/Hook/{$studly}Hook.php",
-        ];
-        if ($withApi) {
-            $planFiles[] = "$base/route/api.php";
-            $planFiles[] = "$base/src/Controller/Api/HelloController.php";
-            $planFiles[] = "$base/src/Controller/Api/ItemController.php";
-        }
-        if ($withAdmin) {
-            $planFiles[] = "$base/route/admin.php";
-            $planFiles[] = "$base/src/Controller/Admin/DashboardController.php";
-            $planFiles[] = "$base/src/Controller/Admin/ManageController.php";
-            $planFiles[] = "$base/src/Controller/Admin/ItemController.php";
-            $planFiles[] = "$base/src/Controller/Admin/UploadController.php";
-        }
-        if ($withService) {
-            $planFiles[] = "$base/src/Service/{$studly}Service.php";
-        }
-        if ($withEntity) {
-            $planFiles[] = "$base/src/Entity/{$studly}.php";
-        }
-        if ($withMigration) {
-            $planFiles[] = "$base/config/install.sql";
-            $planFiles[] = "$base/config/uninstall.sql";
-        }
-
-        if ($dryRun) {
-            $output->writeln("[DRY-RUN] 将创建以下目录:");
-            foreach ($dirs as $d) {
-                $output->writeln("  - $d");
-            }
-            $output->writeln("[DRY-RUN] 将创建以下关键文件(部分):");
-            foreach ($planFiles as $f) {
-                $output->writeln("  - $f");
-            }
-            return 0;
-        }
-
-        foreach ($dirs as $d) {
-            @mkdir($d, 0777, true);
-        }
-
-        // info.php
-        // 生成完整 info.php(参考 guimi)
-        $info = [
-            'id' => $module,
-            'name' => $module,
-            // 分类:core|content|shop|other|custom,默认 custom
-            'category' => 'custom',
-            'description' => $desc ?: ($module . ' 扩展模块'),
-            'version' => '0.1.0',
-            'core_version' => '^1.0',
-            'author' => $author ?: 'sixshop',
-            'email' => '',
-            'website' => '',
-            'image' => '',
-            'license' => 'MIT',
-            'keywords' => [],
-            'dependencies' => [],
-            'conflicts' => [],
-            'requires' => [
-                'php' => '>=8.0.0',
-                'extensions' => ['json', 'pdo'],
-            ],
-        ];
-        $infoExport = var_export($info, true);
-        $infoExport = str_replace(['array (', ')'], ['[', ']'], $infoExport);
-        file_put_contents("$base/info.php", "<?php\ndeclare(strict_types=1);\n\nreturn " . $infoExport . ";\n");
-
-        // Extension.php(使用 Nowdoc + sprintf 注入命名空间)
-        $ns = 'SixShop\\Extension\\' . $module;
-        $extClass = sprintf(<<<'PHP'
-<?php
-declare(strict_types=1);
-
-namespace %s;
-
-use SixShop\Core\ExtensionAbstract;
-use think\facade\Db;
-
-class Extension extends ExtensionAbstract
-{
-    protected function getBaseDir(): string
-    {
-        return dirname(__DIR__);
-    }
-
-    public function install(): void
-    {
-        $sqlFile = __DIR__ . '/../config/install.sql';
-        if (is_file($sqlFile)) {
-            $sql = file_get_contents($sqlFile);
-            if ($sql) { Db::execute($sql); }
-        }
-    }
-
-    public function uninstall(): void
-    {
-        $sqlFile = __DIR__ . '/../config/uninstall.sql';
-        if (is_file($sqlFile)) {
-            $sql = file_get_contents($sqlFile);
-            if ($sql) { Db::execute($sql); }
-        }
-    }
-}
-PHP, $ns);
-        file_put_contents("$base/src/Extension.php", $extClass);
-
-        // config.php(form-create 占位)
-        $configPhp = <<<PHP
-<?php
-declare(strict_types=1);
-
-return [
-    'form' => [
-        [
-            'type' => 'input',
-            'field' => 'title',
-            'title' => '标题',
-            'value' => '',
-            'props' => ['placeholder' => '请输入标题'],
-        ],
-    ],
-];
-PHP;
-        file_put_contents("$base/config.php", $configPhp);
-
-        // README
-        file_put_contents("$base/README.md", "# {$module}\n\n自动生成的扩展骨架。\n");
-
-        // 上面已生成 Extension.php,这里不再重复生成
-
-        // Hook 占位(下面统一生成一次)
-
-        // 安装/卸载 SQL 样板
-        if ($withMigration) {
-            $install = "-- 安装 SQL 示例\n";
-            $uninstall = "-- 卸载 SQL 示例\n";
-            file_put_contents("$base/config/install.sql", $install);
-            file_put_contents("$base/config/uninstall.sql", $uninstall);
-        }
-
-        // 路由(注意:系统会自动加 /api/{$module} 或 /admin/{$module} 前缀,这里不需要再包一层模块分组)
-        $apiRoute = sprintf(<<<'PHP'
-<?php
-declare(strict_types=1);
-
-use SixShop\Extension\%s\Controller\Api\ItemController;
-use think\facade\Route;
-
-// 注意:前缀由系统自动添加,这里只写相对路径
-
-// 健康检查
-Route::get('ping', fn() => json(['code' => 0, 'msg' => 'ok', 'data' => ['pong' => true]]))->middleware(['auth']);
-
-// 示例:业务分组-具体动作(放在资源路由之前,避免 :id 冲突)
-Route::group('item', function () {
-    Route::get('info', [ItemController::class, 'info']);
-    Route::post('check', [ItemController::class, 'check']);
-})->middleware(['auth']);
-
-PHP, $module);
-        $adminRoute = sprintf(<<<'PHP'
-<?php
-declare(strict_types=1);
-
-use think\facade\Route;
-use SixShop\Extension\%s\Controller\Admin\DashboardController;
-use SixShop\Extension\%s\Controller\Admin\ItemController;
-use SixShop\Extension\%s\Controller\Admin\UploadController;
-
-// 首页/仪表盘控制器路由(对齐 guimi 写法)
-Route::get('dashboard/stats', [DashboardController::class, 'stats'])->middleware(['auth']);
-// 可按需继续追加:relation-trend / verification-trend / redemption-trend / latest 等
-
-// 通用上传
-Route::post('upload', [UploadController::class, 'handle'])->middleware(['auth']);
-PHP, $module, $module, $module);
-        if ($withApi) {
-            file_put_contents("$base/route/api.php", $apiRoute);
-        }
-        if ($withAdmin) {
-            file_put_contents("$base/route/admin.php", $adminRoute);
-        }
-
-        // 控制器样板
-        if ($withApi) {
-            $apiCtrl = sprintf(<<<'PHP'
-<?php
-declare(strict_types=1);
-
-namespace %s\Controller\Api;
-
-use think\Request; use think\Response;
-
-class HelloController
-{
-    private function uid(Request $r): ?int { return $r->userID ?? ($r->adminID ?? null); }
-    public function index(Request $r): Response
-    {
-        if (!$this->uid($r)) return json(['code'=>401,'msg'=>'未登录']);
-        return json(['code'=>0,'msg'=>'ok','data'=>['now'=>date('c')]]);
-    }
-}
-PHP, $ns);
-            file_put_contents("$base/src/Controller/Api/HelloController.php", $apiCtrl);
-
-            // API 资源控制器
-            $apiItemCtrl = sprintf(<<<'PHP'
-<?php
-declare(strict_types=1);
-
-namespace %s\Controller\Api;
-
-use think\Request; use think\Response;
-
-class ItemController
-{
-    private function uid(Request $r): ?int { return $r->userID ?? ($r->adminID ?? null); }
-
-    public function index(Request $r): Response { return json(['code'=>0,'msg'=>'ok','data'=>['list'=>[], 'total'=>0]]); }
-    public function read(int $id): Response { return json(['code'=>0,'msg'=>'ok','data'=>['id'=>$id]]); }
-    public function save(Request $r): Response { return json(['code'=>0,'msg'=>'ok','data'=>true]); }
-    public function update(int $id, Request $r): Response { return json(['code'=>0,'msg'=>'ok','data'=>true]); }
-    public function delete(int $id): Response { return json(['code'=>0,'msg'=>'ok','data'=>true]); }
-
-    // 具体动作示例(与路由匹配)
-    public function info(Request $r): Response { return json(['code'=>0,'msg'=>'ok','data'=>['info'=>[]]]); }
-    public function check(Request $r): Response { return json(['code'=>0,'msg'=>'ok','data'=>true]); }
-}
-PHP, $ns);
-            file_put_contents("$base/src/Controller/Api/ItemController.php", $apiItemCtrl);
-        }
-        if ($withAdmin) {
-            $adminCtrl = sprintf(<<<'PHP'
-<?php
-declare(strict_types=1);
-
-namespace %s\Controller\Admin;
-
-use think\Request; use think\Response;
-
-class ManageController
-{
-    public function list(Request $r): Response
-    {
-        return json(['code'=>0,'msg'=>'ok','data'=>['list'=>[], 'total'=>0]]);
-    }
-}
-PHP, $ns);
-            file_put_contents("$base/src/Controller/Admin/ManageController.php", $adminCtrl);
-
-            // 首页/仪表盘控制器(对齐 guimi:dashboard/*)
-            $dashboardCtrl = sprintf(<<<'PHP'
-<?php
-declare(strict_types=1);
-
-namespace %s\Controller\Admin;
-
-use think\Response;
-
-class DashboardController
-{
-    public function stats(): Response
-    {
-        // 首页统计占位:可返回卡片统计与趋势入口
-        return json(['code' => 0, 'msg' => 'ok', 'data' => [
-            'cards' => [
-                ['title' => '总数', 'value' => 0],
-            ],
-        ]]);
-    }
-}
-PHP, $ns);
-            file_put_contents("$base/src/Controller/Admin/DashboardController.php", $dashboardCtrl);
-
-            // Admin 资源控制器
-            $adminItemCtrl = sprintf(<<<'PHP'
-<?php
-declare(strict_types=1);
-
-namespace %s\Controller\Admin;
-
-use think\Request; use think\Response;
-
-class ItemController
-{
-    public function index(Request $r): Response { return json(['code'=>0,'msg'=>'ok','data'=>['list'=>[], 'total'=>0]]); }
-    public function read(int $id): Response { return json(['code'=>0,'msg'=>'ok','data'=>['id'=>$id]]); }
-    public function save(Request $r): Response { return json(['code'=>0,'msg'=>'ok','data'=>true]); }
-    public function update(int $id, Request $r): Response { return json(['code'=>0,'msg'=>'ok','data'=>true]); }
-    public function delete(int $id): Response { return json(['code'=>0,'msg'=>'ok','data'=>true]); }
-}
-PHP, $ns);
-            file_put_contents("$base/src/Controller/Admin/ItemController.php", $adminItemCtrl);
-
-            // Admin 上传控制器
-            $uploadCtrl = sprintf(<<<'PHP'
-<?php
-declare(strict_types=1);
-
-namespace %s\Controller\Admin;
-
-use think\Request; use think\Response;
-
-class UploadController
-{
-    public function handle(Request $r): Response
-    {
-        // TODO: 接入实际存储逻辑,返回 { url, name }
-        return json(['code'=>0,'msg'=>'ok','data'=>['url'=>'','name'=>'']]);
-    }
-}
-PHP, $ns);
-            file_put_contents("$base/src/Controller/Admin/UploadController.php", $uploadCtrl);
-        }
-
-        // Service / Entity 占位
-        if ($withService) {
-            $svc = sprintf(<<<'PHP'
-<?php
-declare(strict_types=1);
-
-namespace %s\Service;
-
-class %sService
-{
-    public function ping(): array { return ['pong' => true]; }
-}
-PHP, $ns, $studly);
-            file_put_contents("$base/src/Service/{$studly}Service.php", $svc);
-        }
-        if ($withEntity) {
-            $ent = sprintf(<<<'PHP'
-<?php
-declare(strict_types=1);
-
-namespace %s\Entity;
-
-class %sEntity
-{
-    public const TABLE = 'extension_%s_item';
-}
-PHP, $ns, $studly, $module);
-            file_put_contents("$base/src/Entity/{$studly}Entity.php", $ent);
-        }
-
-        // 迁移 & 安装/卸载 SQL
-        if ($withMigration) {
-            $install = "-- 安装 SQL 示例\n" .
-                "CREATE TABLE IF NOT EXISTS `extension_{$module}_item`(\n" .
-                "  `id` int unsigned NOT NULL AUTO_INCREMENT,\n" .
-                "  `title` varchar(255) NOT NULL DEFAULT '',\n" .
-                "  `created_at` int unsigned NOT NULL DEFAULT 0,\n" .
-                "  PRIMARY KEY (`id`)\n" .
-                ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n";
-            $uninstall = "DROP TABLE IF EXISTS `extension_{$module}_item`;\n";
-            file_put_contents("$base/config/install.sql", $install);
-            file_put_contents("$base/config/uninstall.sql", $uninstall);
-        }
-
-        // Hook 示例(统一在此生成一次)
-        $hook = sprintf(<<<'PHP'
-<?php
-declare(strict_types=1);
-
-namespace %s\Hook;
-
-class %sHook
-{
-    /** 示例:用户登录后 */
-    public function onUserLogin(array $payload): void {}
-}
-PHP, $ns, $studly);
-        file_put_contents("$base/src/Hook/{$studly}Hook.php", $hook);
-
-        // FFI 样板
-        if ($withFFI) {
-            @mkdir("$base/ffi/model", 0777, true);
-            $gomod = sprintf("module %s\n\ngo 1.21\n", $module);
-            file_put_contents("$base/ffi/go.mod", $gomod);
-            $mainGo = <<<'GO'
-package main
-
-// TODO: 实现导出方法
-func main() {}
-GO;
-            file_put_contents("$base/ffi/main.go", $mainGo);
-            $mk = sprintf(<<<'MK'
-.PHONY: build
-build:
-    go build -buildmode=c-shared -o lib_%s.so main.go
-MK, $module);
-            file_put_contents("$base/ffi/Makefile", $mk);
-            $buildSh = <<<'SH'
-#!/usr/bin/env bash
-set -euo pipefail
-cd "$(dirname "$0")/ffi"
-make build
-cd ..
-echo "[提示] 如使用 FFI,请重启 PHP-FPM 并在 Service Adapter 中切换实现"
-SH;
-            file_put_contents("$base/build.sh", $buildSh);
-            @chmod("$base/build.sh", 0755);
-        }
-
-        // 前端 Admin 模板
-        if ($withFrontend) {
-            // 注意:root_path() 指向 backend/ 应用根;我们需要仓库根目录
-            $projectRoot = rtrim(dirname(root_path()), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
-            // 视图目录名使用连字符(kebab-case),避免下划线
-            $feViewName = str_replace('_', '-', $module);
-            $feBase = $projectRoot . 'frontend/admin/src/views/' . $feViewName;
-            @mkdir($feBase . '/components', 0777, true);
-            @mkdir($feBase . '/composables', 0777, true);
-            $indexVue = sprintf(<<<'VUE'
-<template>
-  <div class="%s-page">
-    <a-tabs v-model:activeKey="tab">
-      <a-tab-pane key="dashboard" tab="仪表盘" />
-      <a-tab-pane key="list" tab="列表" />
-      <a-tab-pane key="settings" tab="设置" />
-    </a-tabs>
-    <component :is="currentComp" />
-  </div>
-</template>
-<script setup lang="ts">
-import { ref, computed } from 'vue'
-const tab = ref('dashboard')
-const currentComp = computed(() => {
-  return tab.value === 'list' ? 'ListPanel' : (tab.value === 'settings' ? 'SettingsPanel' : 'DashboardPanel')
-})
-</script>
-VUE, $feViewName);
-            file_put_contents($feBase . '/index.vue', $indexVue);
-            $dash = "<template><div>DashboardPanel - {$feViewName}</div></template>\n";
-            $list = "<template><div>ListPanel - {$feViewName}</div></template>\n";
-            $settings = "<template><div>SettingsPanel - {$feViewName}</div></template>\n";
-            file_put_contents($feBase . '/components/DashboardPanel.vue', $dash);
-            file_put_contents($feBase . '/components/ListPanel.vue', $list);
-            file_put_contents($feBase . '/components/SettingsPanel.vue', $settings);
-            $useApi = sprintf(<<<'TS'
-import request from '@/utils/request'
-
-export function apiGet(url: string, params?: any) { return request.get(url, { params }) }
-export function apiPost(url: string, data?: any) { return request.post(url, data) }
-export const %sApi = {
-  ping: () => apiGet(`/api/%s/ping`),
-}
-TS, $module, $module);
-            file_put_contents($feBase . '/composables/useApi.ts', $useApi);
-        }
-
-        // 提示
-        $output->writeln("<info>扩展骨架已生成:</info> $base");
-        if ($withFrontend) {
-            $output->writeln("<comment>前端模板:</comment> frontend/admin/src/views/{$module}");
-        }
-        $output->writeln("<comment>下一步:</comment> 1) 根据业务完善 Service/Entity 2) 配置路由与菜单 3) 如需 FFI 执行 {$module}/build.sh");
-        return 0;
-    }
-
-    private function studly(string $value): string
-    {
-        $value = str_replace(['-', '_'], ' ', $value);
-        $value = ucwords($value);
-        return str_replace(' ', '', $value);
-    }
-}

+ 1 - 5
src/Hook/CronJobHook.php

@@ -10,17 +10,13 @@ use SixShop\System\Event\CronJobAfterEvent;
 
 class CronJobHook
 {
-    public function __construct(private CronJobLogEntity $cronJobLogEntity)
-    {
-    }
-
     /**
      * 每次任务执行后记录执行日志到数据库
      */
     #[Hook(CronJobAfterEvent::class)]
     public function onJobExecuted(CronJobAfterEvent $event): void
     {
-        $this->cronJobLogEntity->save([
+        CronJobLogEntity::create([
             'name' => $event->name,
             'rule' => $event->rule,
             'duration' => $event->duration,