first commit
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemTool\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Laravel\Ai\Enums\Lab;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Laravel\Ai\Responses\AgentResponse;
|
||||
use Laravel\Ai\Responses\StreamableAgentResponse;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
use Modules\SystemTool\Ai\Agents\TestAgent;
|
||||
use Modules\SystemTool\Settings\AiSettings;
|
||||
|
||||
/**
|
||||
* AI 服务配置管理
|
||||
*/
|
||||
#[RequestAttribute('/system/ai', 'system.ai')]
|
||||
class SysAiController extends BaseController
|
||||
{
|
||||
/** 获取支持的AI */
|
||||
#[GetRoute('/list', 'list')]
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
$default = [Lab::Anthropic, Lab::OpenAI, Lab::Gemini, Lab::Azure, Lab::Bedrock, Lab::Groq, Lab::xAI, Lab::DeepSeek, Lab::Mistral, Lab::Ollama, Lab::OpenRouter ];
|
||||
return $this->success(compact('default'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 AI 配置(从 DB 加载已保存的值,fallback 到 config 文件)
|
||||
*/
|
||||
#[GetRoute('/config', 'config')]
|
||||
public function getConfig(): JsonResponse
|
||||
{
|
||||
$ai = config('ai');
|
||||
|
||||
return $this->success([
|
||||
'default' => $ai['default'] ?? 'openai',
|
||||
'providers' => $ai['providers'] ?? [],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存 AI 配置到数据库
|
||||
*/
|
||||
#[PostRoute('/save', 'save')]
|
||||
public function saveConfig(): JsonResponse
|
||||
{
|
||||
$data = request()->all();
|
||||
|
||||
try {
|
||||
// 默认驱动
|
||||
AiSettings::set('ai.default', $data['default'] ?? 'openai');
|
||||
|
||||
// 各供应商配置
|
||||
$providers = $data['providers'] ?? [];
|
||||
|
||||
// Anthropic
|
||||
if (isset($providers['anthropic'])) {
|
||||
$p = $providers['anthropic'];
|
||||
AiSettings::set('ai.providers.anthropic.key', $p['key'] ?? '');
|
||||
if (isset($p['url'])) {
|
||||
AiSettings::set('ai.providers.anthropic.url', $p['url']);
|
||||
}
|
||||
}
|
||||
|
||||
// Azure
|
||||
if (isset($providers['azure'])) {
|
||||
$p = $providers['azure'];
|
||||
AiSettings::set('ai.providers.azure.key', $p['key'] ?? '');
|
||||
AiSettings::set('ai.providers.azure.url', $p['url'] ?? '');
|
||||
AiSettings::set('ai.providers.azure.api_version', $p['api_version'] ?? '2025-04-01-preview');
|
||||
AiSettings::set('ai.providers.azure.deployment', $p['deployment'] ?? 'gpt-4o');
|
||||
AiSettings::set('ai.providers.azure.embedding_deployment', $p['embedding_deployment'] ?? 'text-embedding-3-small');
|
||||
AiSettings::set('ai.providers.azure.image_deployment', $p['image_deployment'] ?? 'gpt-image-1');
|
||||
}
|
||||
|
||||
// Bedrock
|
||||
if (isset($providers['bedrock'])) {
|
||||
$p = $providers['bedrock'];
|
||||
AiSettings::set('ai.providers.bedrock.region', $p['region'] ?? 'us-east-1');
|
||||
AiSettings::set('ai.providers.bedrock.key', $p['key'] ?? '');
|
||||
AiSettings::set('ai.providers.bedrock.access_key_id', $p['access_key_id'] ?? '');
|
||||
AiSettings::set('ai.providers.bedrock.secret_access_key', $p['secret_access_key'] ?? '');
|
||||
AiSettings::set('ai.providers.bedrock.session_token', $p['session_token'] ?? '');
|
||||
}
|
||||
|
||||
// Cohere
|
||||
if (isset($providers['cohere'])) {
|
||||
AiSettings::set('ai.providers.cohere.key', $providers['cohere']['key'] ?? '');
|
||||
}
|
||||
|
||||
// DeepSeek
|
||||
if (isset($providers['deepseek'])) {
|
||||
AiSettings::set('ai.providers.deepseek.key', $providers['deepseek']['key'] ?? '');
|
||||
}
|
||||
|
||||
// ElevenLabs
|
||||
if (isset($providers['eleven'])) {
|
||||
AiSettings::set('ai.providers.eleven.key', $providers['eleven']['key'] ?? '');
|
||||
}
|
||||
|
||||
// Gemini
|
||||
if (isset($providers['gemini'])) {
|
||||
$p = $providers['gemini'];
|
||||
AiSettings::set('ai.providers.gemini.key', $p['key'] ?? '');
|
||||
if (isset($p['url'])) {
|
||||
AiSettings::set('ai.providers.gemini.url', $p['url']);
|
||||
}
|
||||
}
|
||||
|
||||
// Groq
|
||||
if (isset($providers['groq'])) {
|
||||
AiSettings::set('ai.providers.groq.key', $providers['groq']['key'] ?? '');
|
||||
}
|
||||
|
||||
// Jina
|
||||
if (isset($providers['jina'])) {
|
||||
AiSettings::set('ai.providers.jina.key', $providers['jina']['key'] ?? '');
|
||||
}
|
||||
|
||||
// Mistral
|
||||
if (isset($providers['mistral'])) {
|
||||
AiSettings::set('ai.providers.mistral.key', $providers['mistral']['key'] ?? '');
|
||||
}
|
||||
|
||||
// Ollama
|
||||
if (isset($providers['ollama'])) {
|
||||
$p = $providers['ollama'];
|
||||
AiSettings::set('ai.providers.ollama.key', $p['key'] ?? '');
|
||||
AiSettings::set('ai.providers.ollama.url', $p['url'] ?? 'http://localhost:11434');
|
||||
}
|
||||
|
||||
// OpenAI
|
||||
if (isset($providers['openai'])) {
|
||||
$p = $providers['openai'];
|
||||
AiSettings::set('ai.providers.openai.key', $p['key'] ?? '');
|
||||
if (isset($p['url'])) {
|
||||
AiSettings::set('ai.providers.openai.url', $p['url']);
|
||||
}
|
||||
}
|
||||
|
||||
// OpenRouter
|
||||
if (isset($providers['openrouter'])) {
|
||||
AiSettings::set('ai.providers.openrouter.key', $providers['openrouter']['key'] ?? '');
|
||||
}
|
||||
|
||||
// VoyageAI
|
||||
if (isset($providers['voyageai'])) {
|
||||
AiSettings::set('ai.providers.voyageai.key', $providers['voyageai']['key'] ?? '');
|
||||
}
|
||||
|
||||
// xAI
|
||||
if (isset($providers['xai'])) {
|
||||
AiSettings::set('ai.providers.xai.key', $providers['xai']['key'] ?? '');
|
||||
}
|
||||
|
||||
// 清除 Laravel 配置缓存
|
||||
Artisan::call('config:clear');
|
||||
|
||||
return $this->success('保存成功');
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error('保存失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 AI 供应商连接
|
||||
*/
|
||||
#[PostRoute('/test', 'test')]
|
||||
public function testConnection(Request $request): StreamableAgentResponse | JsonResponse
|
||||
{
|
||||
try {
|
||||
$agent = TestAgent::make()->forUser($request->user());
|
||||
return $agent->stream('Hello, Who are you?');
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error('连接测试失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemTool\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
use Modules\SystemTool\Http\Requests\SysCarouselFormRequest;
|
||||
use Modules\SystemTool\Models\SysCarouselModel;
|
||||
|
||||
/**
|
||||
* 首页轮播图管理
|
||||
*/
|
||||
#[RequestAttribute('/system/carousel', 'system.carousel')]
|
||||
class SysCarouselController extends BaseController
|
||||
{
|
||||
protected array $searchField = [
|
||||
'status' => '=',
|
||||
];
|
||||
|
||||
protected array $quickSearchField = ['title'];
|
||||
|
||||
/** 查询轮播图列表 */
|
||||
#[GetRoute(authorize: 'query')]
|
||||
public function query(Request $request): JsonResponse
|
||||
{
|
||||
$query = SysCarouselModel::query();
|
||||
$data = $this->buildSearch($request->all(), $query)
|
||||
->orderBy('sort', 'desc')
|
||||
->orderBy('id', 'desc')
|
||||
->paginate($request->input('pageSize', 10))
|
||||
->toArray();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/** 创建轮播图 */
|
||||
#[PostRoute(authorize: 'create')]
|
||||
public function create(SysCarouselFormRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$model = SysCarouselModel::create($validated);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 编辑轮播图 */
|
||||
#[PutRoute(
|
||||
route: '/{id}',
|
||||
authorize: 'update',
|
||||
where: ['id' => '[0-9]+']
|
||||
)]
|
||||
public function update(int $id, SysCarouselFormRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$model = SysCarouselModel::find($id);
|
||||
if (empty($model)) {
|
||||
return $this->error('轮播图不存在');
|
||||
}
|
||||
$model->update($validated);
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 删除轮播图 */
|
||||
#[DeleteRoute(
|
||||
route: '/{id}',
|
||||
authorize: 'delete',
|
||||
where: ['id' => '[0-9]+']
|
||||
)]
|
||||
public function delete(int $id): JsonResponse
|
||||
{
|
||||
$model = SysCarouselModel::find($id);
|
||||
if (empty($model)) {
|
||||
return $this->error('轮播图不存在');
|
||||
}
|
||||
$model->delete();
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemTool\Http\Controllers;
|
||||
|
||||
use App\Exceptions\RepositoryException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
use Modules\SystemTool\Http\Requests\SysDictFormRequest;
|
||||
use Modules\SystemTool\Models\SysDictModel;
|
||||
|
||||
/**
|
||||
* 字典管理
|
||||
*/
|
||||
#[RequestAttribute('/system/dict/list', 'system.dict.list')]
|
||||
class SysDictController extends BaseController
|
||||
{
|
||||
protected array $quickSearchField = ['name', 'code', 'describe'];
|
||||
protected array $searchField = [
|
||||
'status' => '='
|
||||
];
|
||||
|
||||
/** 查询字典列表 */
|
||||
#[GetRoute(authorize: 'query')]
|
||||
public function query(Request $request): JsonResponse
|
||||
{
|
||||
$params = $request->all();
|
||||
$pageSize = $params['pageSize'] ?? 10;
|
||||
$query = SysDictModel::query();
|
||||
$data = $this->buildSearch($params, $query)
|
||||
->paginate($pageSize)
|
||||
->toArray();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/** 创建字典 */
|
||||
#[PostRoute(authorize: 'create')]
|
||||
public function create(SysDictFormRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$model = SysDictModel::create($validated);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 编辑字典 */
|
||||
#[PutRoute(
|
||||
route: '/{id}',
|
||||
authorize: 'update',
|
||||
where: ['id' => '[0-9]+']
|
||||
)]
|
||||
public function update(int $id, SysDictFormRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$model = SysDictModel::find($id);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
$model->update($validated);
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 删除字典 */
|
||||
#[DeleteRoute(
|
||||
route: '/{id}',
|
||||
authorize: 'delete',
|
||||
where: ['id' => '[0-9]+']
|
||||
)]
|
||||
public function delete(int $id): JsonResponse
|
||||
{
|
||||
$model = SysDictModel::find($id);
|
||||
if (empty($model)) {
|
||||
throw new RepositoryException('字典不存在');
|
||||
}
|
||||
$count = $model->dictItems()->count();
|
||||
if ($count > 0) {
|
||||
throw new RepositoryException('字典包含子项,请先删除子项!');
|
||||
}
|
||||
$model->delete();
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 获取所有字典数据 */
|
||||
#[GetRoute('/all', false)]
|
||||
public function all(): JsonResponse
|
||||
{
|
||||
$data = SysDictModel::getAllDictWithItems();
|
||||
return $this->success($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemTool\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
use Modules\SystemTool\Http\Requests\SysDictItemFormRequest;
|
||||
use Modules\SystemTool\Models\SysDictItemModel;
|
||||
|
||||
/**
|
||||
* 字典项控制器
|
||||
*/
|
||||
#[RequestAttribute('/system/dict/item', 'system.dict.item')]
|
||||
class SysDictItemController extends BaseController
|
||||
{
|
||||
protected array $quickSearchField = ['label', 'value'];
|
||||
protected array $searchField = [
|
||||
'dict_id' => '=',
|
||||
'status' => '='
|
||||
];
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
/** 查询字典项列表 */
|
||||
#[GetRoute(authorize: 'query')]
|
||||
public function query(Request $request): JsonResponse
|
||||
{
|
||||
$params = $request->all();
|
||||
$pageSize = $params['pageSize'] ?? 10;
|
||||
$query = SysDictItemModel::query();
|
||||
$data = $this->buildSearch($params, $query)
|
||||
->paginate($pageSize)
|
||||
->toArray();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/** 创建字典项 */
|
||||
#[PostRoute(authorize: 'create')]
|
||||
public function create(SysDictItemFormRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$model = SysDictItemModel::create($validated);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 编辑字典项 */
|
||||
#[PutRoute(
|
||||
route: '/{id}',
|
||||
authorize: 'update',
|
||||
where: ['id' => '[0-9]+']
|
||||
)]
|
||||
public function update(int $id, SysDictItemFormRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$model = SysDictItemModel::find($id);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
$model->update($validated);
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 删除字典项 */
|
||||
#[DeleteRoute(
|
||||
route: '/{id}',
|
||||
authorize: 'delete',
|
||||
where: ['id' => '[0-9]+']
|
||||
)]
|
||||
public function delete(int $id): JsonResponse
|
||||
{
|
||||
$model = SysDictItemModel::find($id);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
$model->delete();
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemTool\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
use Modules\SystemTool\Http\Requests\SysFileMoveOrCopyRequest;
|
||||
use Modules\SystemTool\Models\SysFileModel;
|
||||
use Modules\SystemTool\Services\SysFileService;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
/**
|
||||
* 文件列表
|
||||
*/
|
||||
#[RequestAttribute('/system/file/list', 'system.file.list')]
|
||||
class SysFileController extends BaseController
|
||||
{
|
||||
protected array $searchField = [
|
||||
'group_id' => '=',
|
||||
'name' => 'like',
|
||||
'file_type' => '=',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
protected SysFileService $service
|
||||
) {}
|
||||
|
||||
/** 查询文件列表 */
|
||||
#[GetRoute(authorize: 'query')]
|
||||
public function query(Request $request): JsonResponse
|
||||
{
|
||||
$params = $request->all();
|
||||
$pageSize = $params['pageSize'] ?? 10;
|
||||
$query = SysFileModel::query();
|
||||
$data = $this->buildSearch($params, $query)
|
||||
->paginate($pageSize)
|
||||
->toArray();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/** 上传文件 */
|
||||
#[PostRoute('/upload', 'upload')]
|
||||
public function uploadImage(Request $request): JsonResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
'file' => 'required|file',
|
||||
'group_id' => [
|
||||
'nullable', 'integer',
|
||||
function ($attribute, $value, $fail) {
|
||||
if ($value == 0) {
|
||||
return;
|
||||
}
|
||||
if (!DB::table('sys_file_group')->where('id', $value)->exists()) {
|
||||
$fail('所选的分组 ID 不存在。');
|
||||
}
|
||||
},
|
||||
],
|
||||
]);
|
||||
$result = $this->service->upload(
|
||||
$data['file'],
|
||||
$data['group_id'] ?? 0,
|
||||
10,
|
||||
Auth::id()
|
||||
);
|
||||
return $this->success($result);
|
||||
}
|
||||
|
||||
/** 获取回收站文件列表 */
|
||||
#[GetRoute('/trashed', 'trashed')]
|
||||
public function trashed(): JsonResponse
|
||||
{
|
||||
$list = $this->service->getTrashedList(request()->all());
|
||||
return $this->success($list);
|
||||
}
|
||||
|
||||
/** 删除文件(软删除) */
|
||||
#[DeleteRoute('/{id}', authorize: 'delete', where: ['id' => '[0-9]+'])]
|
||||
public function delete(int $id): JsonResponse
|
||||
{
|
||||
$this->service->delete($id);
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 批量删除文件 */
|
||||
#[DeleteRoute('/batch/delete', 'delete')]
|
||||
public function batchDelete(Request $request): JsonResponse
|
||||
{
|
||||
$ids = $request->input('ids', []);
|
||||
$count = $this->service->batchDelete($ids);
|
||||
return $this->success(['count' => $count]);
|
||||
}
|
||||
|
||||
/** 彻底删除文件 */
|
||||
#[DeleteRoute('/force-delete/{id}', 'force-delete', where: ['id' => '[0-9]+'])]
|
||||
public function forceDelete(int $id): JsonResponse
|
||||
{
|
||||
$this->service->forceDelete($id);
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 批量彻底删除文件 */
|
||||
#[DeleteRoute('/batch/force-delete', 'force-delete')]
|
||||
public function batchForceDelete(Request $request): JsonResponse
|
||||
{
|
||||
$ids = $request->input('ids', []);
|
||||
$count = $this->service->batchForceDelete($ids);
|
||||
return $this->success(['count' => $count]);
|
||||
}
|
||||
|
||||
/** 恢复文件 */
|
||||
#[PostRoute('/restore/{id}', 'restore', where: ['id' => '[0-9]+'])]
|
||||
public function restore(int $id): JsonResponse
|
||||
{
|
||||
$this->service->restore($id);
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 批量恢复文件 */
|
||||
#[PostRoute('/batch/restore', 'restore')]
|
||||
public function batchRestore(Request $request): JsonResponse
|
||||
{
|
||||
$ids = $request->input('ids', []);
|
||||
$count = $this->service->batchRestore($ids);
|
||||
return $this->success(['count' => $count]);
|
||||
}
|
||||
|
||||
/** 复制文件 */
|
||||
#[PostRoute('/copy', 'copy')]
|
||||
public function copy(SysFileMoveOrCopyRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->validated();
|
||||
if(! is_array($data['ids'])) {
|
||||
$result = $this->service->copy($data['ids'], $data['group_id']);
|
||||
} else {
|
||||
$result = $this->service->batchCopy($data['ids'], $data['group_id']);
|
||||
}
|
||||
return $this->success($result);
|
||||
}
|
||||
|
||||
/** 移动文件 */
|
||||
#[PostRoute('/move', 'move')]
|
||||
public function move(SysFileMoveOrCopyRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->validated();
|
||||
if(! is_array($data['ids'])) {
|
||||
$result = $this->service->move($data['ids'], $data['group_id']);
|
||||
} else {
|
||||
$result = $this->service->batchMove($data['ids'], $data['group_id']);
|
||||
}
|
||||
return $this->success($result);
|
||||
}
|
||||
|
||||
/** 重命名文件 */
|
||||
#[PutRoute('/rename/{id}', 'rename', where: ['id' => '[0-9]+'])]
|
||||
public function rename(int $id, Request $request): JsonResponse
|
||||
{
|
||||
$newName = $request->input('name');
|
||||
$this->service->rename($id, $newName);
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 下载文件 */
|
||||
#[GetRoute('/download/{id}', false, where: ['id' => '[0-9]+'])]
|
||||
public function download(int $id): StreamedResponse
|
||||
{
|
||||
return $this->service->download($id);
|
||||
}
|
||||
|
||||
/** 清空回收站文件 */
|
||||
#[DeleteRoute('/clean/trashed', 'clean-trashed')]
|
||||
public function cleanTrashed(Request $request): JsonResponse
|
||||
{
|
||||
$count = $this->service->cleanTrashed();
|
||||
return $this->success(['count' => $count]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemTool\Http\Controllers;
|
||||
|
||||
use App\Exceptions\RepositoryException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
use Modules\SystemTool\Http\Requests\SysFileGroupFormRequest;
|
||||
use Modules\SystemTool\Models\SysFileGroupModel;
|
||||
|
||||
/**
|
||||
* 文件分组控制器
|
||||
*/
|
||||
#[RequestAttribute('/system/file/group', 'system.file.group')]
|
||||
class SysFileGroupController extends BaseController
|
||||
{
|
||||
public function __construct() {}
|
||||
|
||||
/** 获取文件分组列表 */
|
||||
#[GetRoute(authorize: 'query')]
|
||||
public function list(): JsonResponse
|
||||
{
|
||||
$query = SysFileGroupModel::query()->orderBy('sort', 'asc');
|
||||
$keywordSearch = request()->input('keywordSearch', '');
|
||||
if (isset($keywordSearch) && $keywordSearch != '') {
|
||||
$query->whereAny(
|
||||
['name'],
|
||||
'like',
|
||||
'%' . str_replace('%', '\%', $keywordSearch) . '%'
|
||||
);
|
||||
return $this->success($query->get()->toArray());
|
||||
}
|
||||
$group = $query->get()->toArray();
|
||||
return $this->success(getTreeData($group));
|
||||
}
|
||||
|
||||
/** 创建文件分组 */
|
||||
#[PostRoute(authorize: 'create')]
|
||||
public function create(SysFileGroupFormRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$model = SysFileGroupModel::create($validated);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 编辑文件分组 */
|
||||
#[PutRoute(
|
||||
route: '/{id}',
|
||||
authorize: 'update',
|
||||
where: ['id' => '[0-9]+']
|
||||
)]
|
||||
public function update(int $id, SysFileGroupFormRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$model = SysFileGroupModel::find($id);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
$model->update($validated);
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 删除文件分组 */
|
||||
#[DeleteRoute(
|
||||
route: '/{id}',
|
||||
authorize: 'delete',
|
||||
where: ['id' => '[0-9]+']
|
||||
)]
|
||||
public function delete(int $id): JsonResponse
|
||||
{
|
||||
$model = SysFileGroupModel::find($id);
|
||||
if (empty($model)) {
|
||||
throw new RepositoryException('Model not found');
|
||||
}
|
||||
if ($model->countFiles > 0) {
|
||||
throw new RepositoryException('该文件夹下存在文件,无法删除');
|
||||
}
|
||||
$model->delete();
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemTool\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
use Modules\SystemTool\Http\Requests\SysGridNavFormRequest;
|
||||
use Modules\SystemTool\Models\SysGridNavModel;
|
||||
|
||||
/**
|
||||
* 首页宫格导航管理
|
||||
*/
|
||||
#[RequestAttribute('/system/grid-nav', 'system.grid-nav')]
|
||||
class SysGridNavController extends BaseController
|
||||
{
|
||||
protected array $searchField = [
|
||||
'status' => '=',
|
||||
];
|
||||
|
||||
protected array $quickSearchField = ['title'];
|
||||
|
||||
/** 查询宫格导航列表 */
|
||||
#[GetRoute(authorize: 'query')]
|
||||
public function query(Request $request): JsonResponse
|
||||
{
|
||||
$query = SysGridNavModel::query();
|
||||
$data = $this->buildSearch($request->all(), $query)
|
||||
->orderBy('sort', 'desc')
|
||||
->orderBy('id', 'desc')
|
||||
->paginate($request->input('pageSize', 10))
|
||||
->toArray();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/** 创建宫格导航 */
|
||||
#[PostRoute(authorize: 'create')]
|
||||
public function create(SysGridNavFormRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$model = SysGridNavModel::create($validated);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 编辑宫格导航 */
|
||||
#[PutRoute(
|
||||
route: '/{id}',
|
||||
authorize: 'update',
|
||||
where: ['id' => '[0-9]+']
|
||||
)]
|
||||
public function update(int $id, SysGridNavFormRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$model = SysGridNavModel::find($id);
|
||||
if (empty($model)) {
|
||||
return $this->error('宫格导航不存在');
|
||||
}
|
||||
$model->update($validated);
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 删除宫格导航 */
|
||||
#[DeleteRoute(
|
||||
route: '/{id}',
|
||||
authorize: 'delete',
|
||||
where: ['id' => '[0-9]+']
|
||||
)]
|
||||
public function delete(int $id): JsonResponse
|
||||
{
|
||||
$model = SysGridNavModel::find($id);
|
||||
if (empty($model)) {
|
||||
return $this->error('宫格导航不存在');
|
||||
}
|
||||
$model->delete();
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemTool\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
|
||||
#[RequestAttribute]
|
||||
class SysIndexController extends BaseController
|
||||
{
|
||||
|
||||
/** 获取首页信息 */
|
||||
#[GetRoute('/index', false)]
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
$web_setting = site_config('web');
|
||||
return $this->success($web_setting);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemTool\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
use Modules\SystemTool\Settings\MailSettings;
|
||||
|
||||
/**
|
||||
* 邮件配置管理
|
||||
*/
|
||||
#[RequestAttribute('/system/mail', 'system.mail')]
|
||||
class SysMailController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取邮件配置(从 DB 加载已保存的值,fallback 到 config 文件)
|
||||
*/
|
||||
#[GetRoute('/config', 'config')]
|
||||
public function getConfig(): JsonResponse
|
||||
{
|
||||
$mail = config('mail');
|
||||
$mode = $mail['default'] ?? 'single';
|
||||
$mailers = [];
|
||||
if($mode === 'failover') {
|
||||
$mailers = $mail['mailers']['failover']['mailers'] ?? [];
|
||||
} elseif ($mode === 'roundrobin') {
|
||||
$mailers = $mail['mailers']['roundrobin']['mailers'] ?? [];
|
||||
}
|
||||
$other = [
|
||||
'mode' => 'single',
|
||||
'mailers' => $mailers
|
||||
];
|
||||
if($mode === 'failover' || $mode == 'roundrobin') {
|
||||
$other['mode'] = $mode;
|
||||
$mail['default'] = $mailers[0] ?? 'smtp';
|
||||
}
|
||||
return $this->success([
|
||||
'other' => $other,
|
||||
'mail' => $mail,
|
||||
'services' => config('services')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存邮件配置到数据库
|
||||
*
|
||||
* 使用 MailSettings::set() 将每个配置项写入 应用配置 表,
|
||||
* 写入后自动更新缓存,并通过全局中间件同步到 config() 运行时。
|
||||
*/
|
||||
#[PostRoute('/save', 'save')]
|
||||
public function saveConfig(): JsonResponse
|
||||
{
|
||||
$data = request()->all();
|
||||
|
||||
// 解析前端提交的数据结构
|
||||
$other = $data['other'] ?? [];
|
||||
$mail = $data['mail'] ?? [];
|
||||
$services = $data['services'] ?? [];
|
||||
|
||||
// 确定模式并构建 mailers 配置
|
||||
$mode = $other['mode'] ?? 'single';
|
||||
$selectedMailers = $other['mailers'] ?? [];
|
||||
|
||||
try {
|
||||
// 邮件默认驱动
|
||||
if ($mode === 'single') {
|
||||
MailSettings::set('mail.default', $mail['default'] ?? 'smtp');
|
||||
} elseif ($mode === 'failover') {
|
||||
MailSettings::set('mail.default', 'failover');
|
||||
MailSettings::set('mail.mailers.failover.mailers', $selectedMailers);
|
||||
} elseif ($mode === 'roundrobin') {
|
||||
MailSettings::set('mail.default', 'roundrobin');
|
||||
MailSettings::set('mail.mailers.roundrobin.mailers', $selectedMailers);
|
||||
}
|
||||
|
||||
// SMTP 配置
|
||||
if (isset($mail['mailers']['smtp'])) {
|
||||
$smtp = $mail['mailers']['smtp'];
|
||||
MailSettings::set('mail.mailers.smtp.host', $smtp['host'] ?? '127.0.0.1');
|
||||
MailSettings::set('mail.mailers.smtp.port', (int) ($smtp['port'] ?? 587));
|
||||
MailSettings::set('mail.mailers.smtp.username', $smtp['username'] ?? '');
|
||||
MailSettings::set('mail.mailers.smtp.password', $smtp['password'] ?? '');
|
||||
}
|
||||
|
||||
// 发件人配置
|
||||
if (isset($mail['from'])) {
|
||||
MailSettings::set('mail.from.address', $mail['from']['address'] ?? '');
|
||||
MailSettings::set('mail.from.name', $mail['from']['name'] ?? '');
|
||||
}
|
||||
|
||||
// 日志驱动配置
|
||||
if (isset($mail['mailers']['log'])) {
|
||||
MailSettings::set('mail.mailers.log.channel', $mail['mailers']['log']['channel'] ?? 'stack');
|
||||
}
|
||||
|
||||
// 第三方服务配置
|
||||
if (isset($services['postmark'])) {
|
||||
MailSettings::set('services.postmark.token', $services['postmark']['token'] ?? '');
|
||||
}
|
||||
if (isset($services['ses'])) {
|
||||
MailSettings::set('services.ses.key', $services['ses']['key'] ?? '');
|
||||
MailSettings::set('services.ses.secret', $services['ses']['secret'] ?? '');
|
||||
MailSettings::set('services.ses.region', $services['ses']['region'] ?? 'us-east-1');
|
||||
MailSettings::set('services.ses.token', $services['ses']['token'] ?? '');
|
||||
}
|
||||
if (isset($services['resend'])) {
|
||||
MailSettings::set('services.resend.key', $services['resend']['key'] ?? '');
|
||||
}
|
||||
if (isset($services['mailgun'])) {
|
||||
MailSettings::set('services.mailgun.domain', $services['mailgun']['domain'] ?? '');
|
||||
MailSettings::set('services.mailgun.secret', $services['mailgun']['secret'] ?? '');
|
||||
MailSettings::set('services.mailgun.endpoint', $services['mailgun']['endpoint'] ?? 'api.mailgun.net');
|
||||
}
|
||||
|
||||
// 清除 Laravel 配置缓存
|
||||
Artisan::call('config:clear');
|
||||
|
||||
return $this->success('保存成功');
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error('保存失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送测试邮件
|
||||
*/
|
||||
#[PostRoute('/test', 'test')]
|
||||
public function sendTest(): JsonResponse
|
||||
{
|
||||
$to = request()->input('to');
|
||||
if (empty($to)) {
|
||||
return $this->error('请输入收件人邮箱');
|
||||
}
|
||||
try {
|
||||
Mail::raw('这是一封来自 Xin Admin 的测试邮件,用于验证邮件服务配置是否正确。', function ($message) use ($to) {
|
||||
$message->to($to)
|
||||
->subject('Xin Admin 邮件配置测试');
|
||||
});
|
||||
return $this->success('测试邮件发送成功');
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error('发送失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemTool\Http\Controllers;
|
||||
|
||||
use App\Exceptions\RepositoryException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
use Modules\SystemTool\Http\Requests\SysSiteConfigGroupFormRequest;
|
||||
use Modules\SystemTool\Models\SysSiteConfigGroupModel;
|
||||
|
||||
/**
|
||||
* 设置分组控制器
|
||||
*/
|
||||
#[RequestAttribute('/system/config/group', 'system.config.group')]
|
||||
class SysSiteConfigGroupController extends BaseController
|
||||
{
|
||||
/** 查询设置分组列表 */
|
||||
#[GetRoute(authorize: 'query')]
|
||||
public function query(Request $request): JsonResponse
|
||||
{
|
||||
$params = $request->all();
|
||||
$query = SysSiteConfigGroupModel::query();
|
||||
|
||||
if (!empty($params['keywordSearch'])) {
|
||||
$query->whereAny(
|
||||
['title', 'remark', 'key'],
|
||||
'like',
|
||||
'%' . str_replace('%', '\%', $params['keywordSearch']) . '%'
|
||||
);
|
||||
}
|
||||
|
||||
$data = $query->get()->toArray();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/** 创建设置分组 */
|
||||
#[PostRoute(authorize: 'create')]
|
||||
public function create(SysSiteConfigGroupFormRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$model = SysSiteConfigGroupModel::create($validated);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 编辑设置分组 */
|
||||
#[PutRoute(
|
||||
route: '/{id}',
|
||||
authorize: 'update',
|
||||
where: ['id' => '[0-9]+']
|
||||
)]
|
||||
public function update(int $id, SysSiteConfigGroupFormRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$model = SysSiteConfigGroupModel::find($id);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
$model->update($validated);
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 删除设置分组 */
|
||||
#[DeleteRoute(
|
||||
route: '/{id}',
|
||||
authorize: 'delete',
|
||||
where: ['id' => '[0-9]+']
|
||||
)]
|
||||
public function delete(int $id): JsonResponse
|
||||
{
|
||||
$model = SysSiteConfigGroupModel::find($id);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
$count = $model->configs()->count();
|
||||
if ($count > 0) {
|
||||
throw new RepositoryException('当前分组有未删除的设置项!');
|
||||
}
|
||||
$model->delete();
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemTool\Http\Controllers;
|
||||
|
||||
use App\Exceptions\RepositoryException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
use Modules\SystemTool\Http\Requests\SysSiteConfigItemsFormRequest;
|
||||
use Modules\SystemTool\Models\SysSiteConfigItemsModel;
|
||||
use Modules\SystemTool\Services\SysSiteConfigService;
|
||||
|
||||
/**
|
||||
* 系统设置
|
||||
*/
|
||||
#[RequestAttribute('/system/config/items', 'system.config.items')]
|
||||
class SysSiteConfigItemsController extends BaseController
|
||||
{
|
||||
protected array $searchField = [
|
||||
'group_id' => '=',
|
||||
];
|
||||
|
||||
/** 查询设置项列表 */
|
||||
#[GetRoute(authorize: 'query')]
|
||||
public function query(Request $request): JsonResponse
|
||||
{
|
||||
$params = $request->all();
|
||||
if (empty($params['group_id'])) {
|
||||
throw new RepositoryException('请选择设置分组');
|
||||
}
|
||||
$query = SysSiteConfigItemsModel::query();
|
||||
$data = $this->buildSearch($params, $query)
|
||||
->orderBy('sort', 'desc')
|
||||
->get()
|
||||
->toArray();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/** 创建设置项 */
|
||||
#[PostRoute(authorize: 'create')]
|
||||
public function create(SysSiteConfigItemsFormRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$model = SysSiteConfigItemsModel::create($validated);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 编辑设置项 */
|
||||
#[PutRoute(
|
||||
route: '/{id}',
|
||||
authorize: 'update',
|
||||
where: ['id' => '[0-9]+']
|
||||
)]
|
||||
public function update(int $id, SysSiteConfigItemsFormRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$model = SysSiteConfigItemsModel::find($id);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
$model->update($validated);
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 删除设置项 */
|
||||
#[DeleteRoute(
|
||||
route: '/{id}',
|
||||
authorize: 'delete',
|
||||
where: ['id' => '[0-9]+']
|
||||
)]
|
||||
public function delete(int $id): JsonResponse
|
||||
{
|
||||
$model = SysSiteConfigItemsModel::find($id);
|
||||
if (empty($model)) {
|
||||
return $this->error();
|
||||
}
|
||||
$model->delete();
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/** 批量保存设置 */
|
||||
#[PutRoute('/save', 'save')]
|
||||
public function save(): JsonResponse
|
||||
{
|
||||
$configs = request()->input('configs');
|
||||
if (empty($configs) || !is_array($configs)) {
|
||||
return $this->error('请提供设置数据');
|
||||
}
|
||||
|
||||
$result = SysSiteConfigService::batchSaveSiteConfig($configs);
|
||||
|
||||
if ($result['success']) {
|
||||
SysSiteConfigService::refreshSiteConfig();
|
||||
return $this->success();
|
||||
}
|
||||
$message = '设置保存失败:' . $result['message'];
|
||||
return $this->error($message);
|
||||
}
|
||||
|
||||
/** 刷新设置 */
|
||||
#[PostRoute('/refreshCache', 'refresh')]
|
||||
public function refreshCache(): JsonResponse
|
||||
{
|
||||
SysSiteConfigService::refreshSiteConfig();
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemTool\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
use Modules\SystemTool\Settings\StorageSettings;
|
||||
|
||||
/**
|
||||
* 文件存储配置管理
|
||||
*/
|
||||
#[RequestAttribute('/system/storage', 'system.storage')]
|
||||
class SysStorageController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取存储配置(从 DB 加载已保存的值,fallback 到 config 文件)
|
||||
*/
|
||||
#[GetRoute('/config', 'config')]
|
||||
public function getConfig(): JsonResponse
|
||||
{
|
||||
$filesystems = config('filesystems');
|
||||
$default = $filesystems['default'] ?? 'local';
|
||||
|
||||
// 获取各驱动配置
|
||||
$disks = $filesystems['disks'] ?? [];
|
||||
|
||||
// 本地存储配置
|
||||
$local = [
|
||||
'root' => $disks['local']['root'] ?? storage_path('app/public'),
|
||||
'url' => $disks['local']['url'] ?? config('app.url') . '/storage',
|
||||
'visibility' => $disks['local']['visibility'] ?? 'public',
|
||||
];
|
||||
|
||||
// S3 / OSS 配置
|
||||
$s3 = [
|
||||
'key' => $disks['s3']['key'] ?? '',
|
||||
'secret' => $disks['s3']['secret'] ?? '',
|
||||
'region' => $disks['s3']['region'] ?? '',
|
||||
'bucket' => $disks['s3']['bucket'] ?? '',
|
||||
'url' => $disks['s3']['url'] ?? '',
|
||||
'endpoint' => $disks['s3']['endpoint'] ?? '',
|
||||
'use_path_style_endpoint' => $disks['s3']['use_path_style_endpoint'] ?? false,
|
||||
];
|
||||
|
||||
// FTP 配置
|
||||
$ftp = [
|
||||
'host' => $disks['ftp']['host'] ?? '',
|
||||
'username' => $disks['ftp']['username'] ?? '',
|
||||
'password' => $disks['ftp']['password'] ?? '',
|
||||
'port' => $disks['ftp']['port'] ?? 21,
|
||||
'root' => $disks['ftp']['root'] ?? '',
|
||||
'passive' => $disks['ftp']['passive'] ?? true,
|
||||
'ssl' => $disks['ftp']['ssl'] ?? false,
|
||||
'timeout' => $disks['ftp']['timeout'] ?? 30,
|
||||
];
|
||||
|
||||
// SFTP 配置
|
||||
$sftp = [
|
||||
'host' => $disks['sftp']['host'] ?? '',
|
||||
'username' => $disks['sftp']['username'] ?? '',
|
||||
'password' => $disks['sftp']['password'] ?? '',
|
||||
'port' => $disks['sftp']['port'] ?? 22,
|
||||
'root' => $disks['sftp']['root'] ?? '',
|
||||
'timeout' => $disks['sftp']['timeout'] ?? 30,
|
||||
'private_key' => $disks['sftp']['privateKey'] ?? '',
|
||||
'passphrase' => $disks['sftp']['passphrase'] ?? '',
|
||||
];
|
||||
|
||||
return $this->success([
|
||||
'default' => $default,
|
||||
'local' => $local,
|
||||
's3' => $s3,
|
||||
'ftp' => $ftp,
|
||||
'sftp' => $sftp,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存存储配置到数据库
|
||||
*
|
||||
* 使用 StorageSettings::set() 将每个配置项写入 应用配置 表,
|
||||
* 写入后自动更新缓存,并通过全局中间件同步到 config() 运行时。
|
||||
*/
|
||||
#[PostRoute('/save', 'save')]
|
||||
public function saveConfig(): JsonResponse
|
||||
{
|
||||
$data = request()->all();
|
||||
|
||||
$default = $data['default'] ?? 'local';
|
||||
$local = $data['local'] ?? [];
|
||||
$s3 = $data['s3'] ?? [];
|
||||
$ftp = $data['ftp'] ?? [];
|
||||
$sftp = $data['sftp'] ?? [];
|
||||
|
||||
try {
|
||||
// 默认存储驱动
|
||||
StorageSettings::set('filesystems.default', $default);
|
||||
|
||||
// 本地存储
|
||||
if (!empty($local['url'])) {
|
||||
StorageSettings::set('filesystems.disks.local.url', $local['url']);
|
||||
}
|
||||
|
||||
// S3
|
||||
if ($default === 's3' || !empty($s3['key'])) {
|
||||
StorageSettings::set('filesystems.disks.s3.key', $s3['key'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.s3.secret', $s3['secret'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.s3.region', $s3['region'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.s3.bucket', $s3['bucket'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.s3.url', $s3['url'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.s3.endpoint', $s3['endpoint'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.s3.use_path_style_endpoint', (bool) ($s3['use_path_style_endpoint'] ?? false));
|
||||
}
|
||||
|
||||
// FTP
|
||||
if ($default === 'ftp' || !empty($ftp['host'])) {
|
||||
StorageSettings::set('filesystems.disks.ftp.host', $ftp['host'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.ftp.username', $ftp['username'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.ftp.password', $ftp['password'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.ftp.port', (int) ($ftp['port'] ?? 21));
|
||||
StorageSettings::set('filesystems.disks.ftp.root', $ftp['root'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.ftp.passive', (bool) ($ftp['passive'] ?? true));
|
||||
StorageSettings::set('filesystems.disks.ftp.ssl', (bool) ($ftp['ssl'] ?? false));
|
||||
StorageSettings::set('filesystems.disks.ftp.timeout', (int) ($ftp['timeout'] ?? 30));
|
||||
}
|
||||
|
||||
// SFTP
|
||||
if ($default === 'sftp' || !empty($sftp['host'])) {
|
||||
StorageSettings::set('filesystems.disks.sftp.host', $sftp['host'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.sftp.username', $sftp['username'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.sftp.password', $sftp['password'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.sftp.port', (int) ($sftp['port'] ?? 22));
|
||||
StorageSettings::set('filesystems.disks.sftp.root', $sftp['root'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.sftp.timeout', (int) ($sftp['timeout'] ?? 30));
|
||||
StorageSettings::set('filesystems.disks.sftp.privateKey', $sftp['private_key'] ?? '');
|
||||
StorageSettings::set('filesystems.disks.sftp.passphrase', $sftp['passphrase'] ?? '');
|
||||
}
|
||||
|
||||
// 清除 Laravel 配置缓存
|
||||
Artisan::call('config:clear');
|
||||
|
||||
return $this->success('保存成功');
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error('保存失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试存储连接
|
||||
*/
|
||||
#[PostRoute('/test', 'test')]
|
||||
public function testConnection(): JsonResponse
|
||||
{
|
||||
$disk = request()->input('disk', 'local');
|
||||
|
||||
try {
|
||||
$storage = Storage::disk($disk);
|
||||
$testFile = 'storage_test_' . time() . '.txt';
|
||||
$testContent = 'XinAdmin 存储测试文件 - ' . date('Y-m-d H:i:s');
|
||||
|
||||
// 测试写入
|
||||
$storage->put($testFile, $testContent);
|
||||
|
||||
// 测试读取
|
||||
$readContent = $storage->get($testFile);
|
||||
if ($readContent !== $testContent) {
|
||||
return $this->error('读取测试失败:内容不匹配');
|
||||
}
|
||||
|
||||
// 测试删除
|
||||
$storage->delete($testFile);
|
||||
|
||||
return $this->success('存储连接测试成功');
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error('连接测试失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user