feat: 发布智能体版

This commit is contained in:
augushong
2026-03-26 20:22:34 +08:00
parent 7ee9e102a5
commit 8cc08bcb8c
138 changed files with 7964 additions and 660 deletions

View File

@@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
namespace base\common\command\admin\menu;
use app\admin\model\SystemMenu;
use app\common\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
/**
* admin:menu:create command - 创建菜单
*/
class AdminMenuCreateBase extends Command
{
protected function configure()
{
parent::configure();
// 命令配置
$this->setName('admin:menu:create')
->addOption('title', null, Option::VALUE_REQUIRED, '菜单标题(必填)')
->addOption('path', null, Option::VALUE_OPTIONAL, '菜单路径', '')
->addOption('icon', null, Option::VALUE_OPTIONAL, '菜单图标', '')
->addOption('parent-id', null, Option::VALUE_OPTIONAL, '父菜单ID默认为0', 0)
->addOption('sort', null, Option::VALUE_OPTIONAL, '排序默认为100', 100)
->addOption('node', null, Option::VALUE_OPTIONAL, '权限节点', '')
->addOption('remark', null, Option::VALUE_OPTIONAL, '备注说明', '')
->setDescription('创建菜单');
}
protected function execute(Input $input, Output $output)
{
// 获取参数
$title = $input->getOption('title');
$path = $input->getOption('path');
$icon = $input->getOption('icon');
$parentId = $input->getOption('parent-id');
$sort = $input->getOption('sort');
$node = $input->getOption('node');
$remark = $input->getOption('remark');
// 验证必填参数
if (empty($title)) {
$output->error('菜单标题不能为空');
return false;
}
try {
// 准备菜单数据
$menuData = [
'title' => $title,
'path' => $path,
'icon' => $icon,
'parent_id' => (int)$parentId,
'sort' => (int)$sort,
'node' => $node,
'remark' => $remark,
];
// 实例化菜单服务(使用 app\admin\service\MenuService它继承自 base\common\service\MenuServiceBase
// 由于 MenuServiceBase 需要 adminId 参数,我们使用 0 表示命令行操作
$menuService = new \app\common\service\MenuService(0);
// 创建菜单
$menuId = $menuService->create($menuData);
// 获取创建的菜单详情
$menuModel = SystemMenu::find($menuId);
$menu = $menuModel ? $menuModel->toArray() : null;
if (empty($menu)) {
throw new \Exception('菜单创建成功,但无法获取菜单详情');
}
$outputData = [
'id' => (int)$menu['id'],
'title' => $menu['title'] ?? '',
'path' => $menu['href'] ?? '',
'icon' => $menu['icon'] ?? '',
'parent_id' => (int)($menu['pid'] ?? 0),
'sort' => (int)($menu['sort'] ?? 0),
'node' => $menu['auth_node'] ?? '',
];
// 输出结果
$output->info('菜单创建成功');
$output->info('菜单ID: ' . $outputData['id']);
$output->info('菜单标题: ' . $outputData['title']);
$output->info('菜单路径: ' . $outputData['path']);
$output->info('菜单图标: ' . $outputData['icon']);
$output->info('父菜单ID: ' . $outputData['parent_id']);
$output->info('排序: ' . $outputData['sort']);
if (!empty($outputData['node'])) {
$output->info('权限节点: ' . $outputData['node']);
}
} catch (\Throwable $e) {
$output->error('创建菜单失败: ' . $e->getMessage());
return false;
}
return true;
}
}

View File

@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace base\common\command\admin\menu;
use app\admin\model\SystemMenu;
use app\common\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
/**
* admin:menu:delete command - 删除菜单
*/
class AdminMenuDeleteBase extends Command
{
protected function configure()
{
parent::configure();
// 指令配置
$this->setName('admin:menu:delete')
->addOption('id', null, Option::VALUE_REQUIRED, '菜单ID')
->setDescription('删除菜单');
}
protected function execute(Input $input, Output $output)
{
// 获取参数
$id = $input->getOption('id');
// 验证参数
if (empty($id)) {
$output->error('菜单ID不能为空');
return false;
}
try {
// 1. 验证菜单是否存在
$menu = SystemMenu::find($id);
if (empty($menu)) {
$output->error('菜单ID ' . $id . ' 不存在');
return false;
}
// 2. 检查是否有子菜单
$childCount = SystemMenu::where('pid', $id)
->where('delete_time', 0)
->count();
if ($childCount > 0) {
$output->error('菜单ID ' . $id . ' 存在 ' . $childCount . ' 个子菜单,请先删除子菜单');
return false;
}
// 3. 保存菜单信息用于输出
$menuInfo = [
'id' => (int)$menu->id,
'title' => $menu->title,
'path' => $menu->href ?? '',
'icon' => $menu->icon ?? '',
'parent_id' => (int)($menu->pid ?? 0),
'sort' => (int)($menu->sort ?? 0),
'node' => $menu->auth_node ?? '',
];
// 4. 执行删除(软删除)
$menu->delete();
// 5. 输出结果
$output->info('菜单删除成功');
$output->info('菜单ID: ' . $menuInfo['id']);
$output->info('菜单名称: ' . $menuInfo['title']);
$output->info('菜单路径: ' . $menuInfo['path']);
} catch (\Throwable $e) {
$output->error('删除菜单失败: ' . $e->getMessage());
return false;
}
return true;
}
}

View File

@@ -0,0 +1,127 @@
<?php
declare(strict_types=1);
namespace base\common\command\admin\menu;
use app\admin\model\SystemMenu;
use app\common\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
/**
* admin:menu:export command - 导出菜单
*/
class AdminMenuExportBase extends Command
{
protected function configure()
{
parent::configure();
// 指令配置
$this->setName('admin:menu:export')
->setDescription('导出菜单数据')
->addOption('format', null, Option::VALUE_OPTIONAL, '输出格式text/json', 'text')
->addOption('output', null, Option::VALUE_OPTIONAL, '输出文件路径(可选)');
}
protected function execute(Input $input, Output $output)
{
// 获取参数
$outputPath = $input->getOption('output');
try {
// 1. 查询所有菜单
$menus = SystemMenu::where('delete_time', 0)
->order(['sort' => 'desc', 'id' => 'asc'])
->select()
->hidden(['create_time', 'update_time', 'delete_time']);
// 2. 格式化菜单数据
$menuData = [];
foreach ($menus as $menu) {
$menuData[] = [
'id' => (int)$menu->id,
'pid' => (int)($menu->pid ?? 0),
'title' => $menu->title ?? '',
'href' => $menu->href ?? '',
'icon' => $menu->icon ?? '',
'sort' => (int)($menu->sort ?? 0),
'auth_node' => $menu->auth_node ?? '',
'status' => (int)($menu->status ?? 0),
'type' => (int)($menu->type ?? 0),
];
}
// JSON 模式输出
if ($input->getOption('format') === 'json') {
if (!empty($outputPath)) {
$jsonData = json_encode($menuData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
if ($jsonData === false) {
throw new \RuntimeException('JSON 编码失败');
}
$result = file_put_contents($outputPath, $jsonData);
if ($result === false) {
throw new \RuntimeException('无法写入文件: ' . $outputPath);
}
}
$json = json_encode([
'success' => true,
'data' => [
'menus' => $menuData,
'output' => !empty($outputPath) ? $outputPath : null,
],
'warnings' => [],
'metadata' => [
'count' => count($menuData),
'exported_at' => date('c'),
],
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$output->writeln($json);
return true;
}
// 3. 如果指定了输出路径,写入文件
if (!empty($outputPath)) {
$jsonData = json_encode($menuData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
$result = file_put_contents($outputPath, $jsonData);
if ($result === false) {
$output->error('无法写入文件: ' . $outputPath);
return false;
}
$output->info('成功导出 ' . count($menuData) . ' 个菜单到文件: ' . $outputPath);
} else {
// 4. 输出结果到控制台
$output->info('成功导出 ' . count($menuData) . ' 个菜单');
// 以表格形式输出菜单数据
$output->writeln('菜单ID | 父菜单ID | 菜单标题 | 菜单路径 | 图标 | 排序 | 权限节点 | 状态 | 类型');
$output->writeln('--------|----------|----------|----------|------|------|----------|------|------');
foreach ($menuData as $menu) {
$output->writeln($menu['id'] . ' | ' .
$menu['pid'] . ' | ' .
$menu['title'] . ' | ' .
$menu['href'] . ' | ' .
$menu['icon'] . ' | ' .
$menu['sort'] . ' | ' .
$menu['auth_node'] . ' | ' .
$menu['status'] . ' | ' .
$menu['type']);
}
}
} catch (\Throwable $e) {
$output->error('导出菜单失败: ' . $e->getMessage());
return false;
}
return true;
}
}

View File

@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace base\common\command\admin\menu;
use app\admin\model\SystemMenu;
use app\common\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
class AdminMenuListBase extends Command
{
protected function configure()
{
parent::configure();
$this->setName('admin:menu:list')
->setDescription('列出所有菜单(树形结构)')
->addOption('format', null, Option::VALUE_OPTIONAL, '输出格式tree/table/json', 'tree')
->addOption('status', null, Option::VALUE_OPTIONAL, '筛选状态0=禁用1=启用all=全部)', 'all')
->addOption('pid', null, Option::VALUE_OPTIONAL, '指定父菜单ID默认从根菜单开始', null);
}
protected function execute(Input $input, Output $output)
{
$format = $input->getOption('format');
$statusFilter = $input->getOption('status');
$pidFilter = $input->getOption('pid');
try {
$query = SystemMenu::where('delete_time', 0);
if ($statusFilter !== 'all') {
$query->where('status', (int)$statusFilter);
}
$menus = $query->order(['sort' => 'desc', 'id' => 'asc'])
->select()
->toArray();
if (empty($menus)) {
$output->comment('没有找到菜单数据');
return true;
}
$menuTree = $this->buildTree($menus, $pidFilter ? (int)$pidFilter : 0);
switch ($format) {
case 'json':
$this->outputJson($output, $menuTree, count($menus));
break;
case 'table':
$this->outputTable($output, $menus);
break;
case 'tree':
default:
$this->outputTree($output, $menuTree);
break;
}
} catch (\Throwable $e) {
$output->error('获取菜单列表失败: ' . $e->getMessage());
return false;
}
return true;
}
protected function buildTree(array $menus, int $pid = 0): array
{
$tree = [];
foreach ($menus as $menu) {
if ((int)$menu['pid'] === $pid) {
$children = $this->buildTree($menus, (int)$menu['id']);
if (!empty($children)) {
$menu['children'] = $children;
}
$tree[] = $menu;
}
}
return $tree;
}
protected function outputTree(Output $output, array $tree, int $level = 0): void
{
$prefix = str_repeat(' ', $level);
$branch = $level > 0 ? '├─ ' : '';
foreach ($tree as $node) {
$status = $node['status'] ? '<info>●</info>' : '<comment>○</comment>';
$href = $node['href'] ?? '-';
$output->writeln("{$prefix}{$branch}{$status} [{$node['id']}] {$node['title']} <comment>({$href})</comment>");
if (!empty($node['children'])) {
$this->outputTree($output, $node['children'], $level + 1);
}
}
}
protected function outputTable(Output $output, array $menus): void
{
$output->writeln('ID | PID | 标题 | 路径 | 状态');
$output->writeln('---|-----|------|------|------');
foreach ($menus as $menu) {
$status = $menu['status'] ? '启用' : '禁用';
$output->writeln(sprintf(
'%d | %d | %s | %s | %s',
$menu['id'],
$menu['pid'],
$menu['title'],
$menu['href'] ?? '-',
$status
));
}
$output->writeln('');
$output->info('共 ' . count($menus) . ' 个菜单');
}
protected function outputJson(Output $output, array $tree, int $total): void
{
$data = [
'success' => true,
'data' => ['menus' => $tree],
'metadata' => ['total' => $total],
];
$output->writeln(json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
}

View File

@@ -0,0 +1,129 @@
<?php
declare(strict_types=1);
namespace base\common\command\admin\menu;
use app\admin\model\SystemMenu;
use app\common\console\Command;
use app\common\service\MenuService;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
/**
* admin:menu:update command - 更新菜单
*/
class AdminMenuUpdateBase extends Command
{
protected function configure()
{
parent::configure();
// 指令配置
$this->setName('admin:menu:update')
->addOption('id', null, Option::VALUE_REQUIRED, '菜单ID必填')
->addOption('title', null, Option::VALUE_OPTIONAL, '菜单标题')
->addOption('path', null, Option::VALUE_OPTIONAL, '菜单路径')
->addOption('icon', null, Option::VALUE_OPTIONAL, '菜单图标')
->addOption('parent-id', null, Option::VALUE_OPTIONAL, '父级菜单ID')
->addOption('sort', null, Option::VALUE_OPTIONAL, '排序')
->setDescription('编辑菜单');
}
protected function execute(Input $input, Output $output)
{
// 获取参数
$id = (int)($input->getOption('id') ?? 0);
$title = $input->getOption('title');
$path = $input->getOption('path');
$icon = $input->getOption('icon');
$parentId = $input->getOption('parent-id');
$sort = $input->getOption('sort');
// 验证参数id 是必填的
if (empty($id)) {
$output->error('菜单ID不能为空');
return false;
}
try {
// 1. 验证菜单是否存在
$menu = SystemMenu::find($id);
if (empty($menu)) {
$output->writeln('<error>菜单ID ' . $id . ' 不存在</error>');
return false;
}
// 2. 准备更新数据
$updateData = [];
if ($title !== null) {
$updateData['title'] = $title;
}
if ($path !== null) {
$updateData['path'] = $path;
}
if ($icon !== null) {
$updateData['icon'] = $icon;
}
if ($parentId !== null) {
$updateData['parent_id'] = (int)$parentId;
}
if ($sort !== null) {
$updateData['sort'] = (int)$sort;
}
// 检查是否有需要更新的字段
if (empty($updateData)) {
$output->error('没有提供需要更新的字段');
return false;
}
// 3. 调用 MenuService 更新菜单
$menuService = new MenuService(0);
$success = $menuService->update($id, $updateData);
if (!$success) {
$output->writeln('<error>菜单更新失败</error>');
return false;
}
// 4. 重新查询菜单数据
$updatedMenu = SystemMenu::find($id);
if (empty($updatedMenu)) {
$output->writeln('<error>菜单更新后查询失败</error>');
return false;
}
$outputData = [
'id' => (int)$updatedMenu->id,
'title' => $updatedMenu->title ?? '',
'path' => $updatedMenu->href ?? '',
'icon' => $updatedMenu->icon ?? '',
'parent_id' => (int)($updatedMenu->pid ?? 0),
'sort' => (int)($updatedMenu->sort ?? 0),
'node' => $updatedMenu->auth_node ?? '',
];
// 5. 输出结果
$output->writeln('<info>菜单编辑成功</info>');
$output->writeln('<info>菜单ID: ' . $outputData['id'] . '</info>');
$output->writeln('<info>菜单标题: ' . $outputData['title'] . '</info>');
$output->writeln('<info>菜单路径: ' . $outputData['path'] . '</info>');
$output->writeln('<info>菜单图标: ' . $outputData['icon'] . '</info>');
$output->writeln('<info>父级菜单ID: ' . $outputData['parent_id'] . '</info>');
$output->writeln('<info>排序: ' . $outputData['sort'] . '</info>');
} catch (\Throwable $e) {
$output->error('编辑菜单失败: ' . $e->getMessage());
return false;
}
return true;
}
}