初始化本地仓库
This commit is contained in:
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi;
|
||||
|
||||
use app\model\system\Admin;
|
||||
use app\scope\global\AccessPermissionScope;
|
||||
use app\service\admin\system\AdminService;
|
||||
use core\cache\CacheService;
|
||||
use core\exception\handler\ForbiddenHttpException;
|
||||
use core\jwt\JwtToken;
|
||||
use core\logger\Logger;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use support\Container;
|
||||
|
||||
final readonly class CurrentUser
|
||||
{
|
||||
// 缓存键前缀
|
||||
public const CACHE_PREFIX = 'admin_user';
|
||||
|
||||
// 缓存过期时间(秒)
|
||||
public const CACHE_EXPIRE = 3600;
|
||||
|
||||
private AdminService $service;
|
||||
private CacheService $cache;
|
||||
|
||||
public function __construct(AdminService $service, CacheService $cache)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成管理员缓存键
|
||||
* @param int|string $uid 管理员ID
|
||||
* @return string 缓存键
|
||||
*/
|
||||
public static function generateCacheKey(int|string $uid): string
|
||||
{
|
||||
return self::CACHE_PREFIX . '_' . $uid;
|
||||
}
|
||||
|
||||
public function admin(bool $toArray = false): null|Admin|array
|
||||
{
|
||||
if (!$this->getToken()) {
|
||||
return null;
|
||||
}
|
||||
$uid = $this->id();
|
||||
$cacheKey = self::generateCacheKey($uid);
|
||||
|
||||
// 使用缓存服务获取管理员信息
|
||||
$admin = $this->cache->remember($cacheKey, function () use ($uid) {
|
||||
return $this->service->get($uid, ['*'], [], '', [AccessPermissionScope::class]);
|
||||
}, self::CACHE_EXPIRE);
|
||||
|
||||
return $toArray && $admin ? $admin->toArray() : $admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除管理员缓存
|
||||
* @param int|string|null $uid 管理员ID,默认清除当前用户缓存
|
||||
*/
|
||||
public function clearCache(int|string|null $uid = null): void
|
||||
{
|
||||
$uid = $uid ?? $this->id();
|
||||
if ($uid) {
|
||||
$cacheKey = self::generateCacheKey($uid);
|
||||
$this->cache->delete($cacheKey);
|
||||
|
||||
// 同时清除权限缓存
|
||||
$permissionCacheKey = $cacheKey . '_permissions';
|
||||
$this->cache->delete($permissionCacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
public function refresh(): array
|
||||
{
|
||||
if (!$this->getToken()) {
|
||||
Logger::debug("当前用户无有效 Token,无法刷新");
|
||||
return [];
|
||||
}
|
||||
return (new JwtToken())->refresh()->toArray();
|
||||
}
|
||||
|
||||
public function id(): int|string
|
||||
{
|
||||
$token = $this->getToken();
|
||||
if (!$token) {
|
||||
return 0;
|
||||
}
|
||||
$aid = (new JwtToken())->id();
|
||||
if ($aid === null) {
|
||||
return 0;
|
||||
}
|
||||
return $aid ?? 0;
|
||||
}
|
||||
|
||||
public function isSuperAdmin(): bool
|
||||
{
|
||||
$admin = $this->admin();
|
||||
return $admin && $admin->isSuperAdmin();
|
||||
}
|
||||
|
||||
public function getToken(): ?string
|
||||
{
|
||||
$request = request();
|
||||
if (empty($request)) {
|
||||
return null;
|
||||
}
|
||||
$tokenName = config('core.jwt.app.token_name', 'Authorization');
|
||||
$authorization = $request->header($tokenName);
|
||||
if (empty($authorization) || $authorization === 'undefined') {
|
||||
$authorization = $request->get('token');
|
||||
}
|
||||
if (!$authorization || $authorization === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
if (count(explode(' ', $authorization)) !== 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
[$type, $token] = explode(' ', $authorization);
|
||||
|
||||
if ($type !== 'Bearer') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$token || $token === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成新令牌
|
||||
* @param array $userInfo 用户信息
|
||||
* @param string $type 类型
|
||||
* @return array 令牌信息
|
||||
*/
|
||||
public function generateToken(array $userInfo, string $type = 'admin'): array
|
||||
{
|
||||
$jwt = new JwtToken();
|
||||
$tokenObj = $jwt->generate((string)$this->id(), $type, $userInfo);
|
||||
return [
|
||||
'access_token' => $tokenObj->accessToken,
|
||||
'refresh_token' => $tokenObj->refreshToken,
|
||||
'expires_in' => $tokenObj->expiresIn,
|
||||
'expires_time' => time() + $tokenObj->expiresIn
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 登出(将令牌加入黑名单)
|
||||
* @param string|null $token 令牌,默认使用当前请求的令牌
|
||||
* @return bool 是否成功
|
||||
*/
|
||||
public function logout(?string $token = null): bool
|
||||
{
|
||||
$token = $token ?? $this->getToken();
|
||||
if (!$token) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
(new JwtToken())->logout($token);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
Logger::error("登出失败: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取令牌负载信息
|
||||
* @return array 负载信息
|
||||
*/
|
||||
public function getPayload(): array
|
||||
{
|
||||
try {
|
||||
$jwt = new JwtToken();
|
||||
// 使用 getPayloadFromRequest() 方法获取负载信息
|
||||
$payload = $jwt->getPayloadFromRequest();
|
||||
return $payload['ext'] ?? $payload;
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户所有菜单权限码
|
||||
*
|
||||
* @return array 权限码数组
|
||||
*/
|
||||
public function getPermissions(): array
|
||||
{
|
||||
$admin = $this->admin();
|
||||
if (!$admin) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($admin->isSuperAdmin()) {
|
||||
return ['*']; // 超级管理员拥有所有权限
|
||||
}
|
||||
|
||||
$uid = $this->id();
|
||||
$cacheKey = self::generateCacheKey($uid) . '_permissions';
|
||||
|
||||
// 使用缓存服务获取权限信息
|
||||
return $this->cache->remember($cacheKey, function () use ($admin) {
|
||||
$permissions = [];
|
||||
// 通过角色获取菜单权限码
|
||||
$roles = $admin->roles()->with('menus')->get();
|
||||
|
||||
foreach ($roles as $role) {
|
||||
foreach ($role->menus as $menu) {
|
||||
if (!empty($menu->code)) {
|
||||
$permissions[] = $menu->code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($permissions);
|
||||
}, self::CACHE_EXPIRE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否有权限
|
||||
*
|
||||
* @param string|array $codes 权限码,可以是字符串或数组
|
||||
* @param string $operation 操作类型,支持 'and' 或 'or'
|
||||
* @return bool 是否有权限
|
||||
*/
|
||||
public function hasPermission(string|array $codes, string $operation = 'and'): bool
|
||||
{
|
||||
// 如果是超级管理员,直接返回true
|
||||
if ($this->isSuperAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$codes = is_array($codes) ? $codes : [$codes];
|
||||
$userPermissions = $this->getPermissions();
|
||||
|
||||
// 如果有通配符权限,直接返回true
|
||||
if (in_array('*', $userPermissions)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$operation = strtolower($operation);
|
||||
|
||||
if ($operation === Permission::OPERATION_AND) {
|
||||
// AND模式:所有权限码都必须有
|
||||
foreach ($codes as $code) {
|
||||
if (!in_array($code, $userPermissions)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if ($operation === Permission::OPERATION_OR) {
|
||||
// OR模式:至少有一个权限码
|
||||
foreach ($codes as $code) {
|
||||
if (in_array($code, $userPermissions)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException("不支持的操作类型: {$operation}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否有权限,没有权限时抛出异常
|
||||
*
|
||||
* @param string|array $codes 权限码,可以是字符串或数组
|
||||
* @param string $operation 操作类型,支持 'and' 或 'or'
|
||||
* @throws ForbiddenHttpException 没有权限时抛出异常
|
||||
*/
|
||||
public function checkPermission(string|array $codes, string $operation = 'and'): void
|
||||
{
|
||||
if (!$this->hasPermission($codes, $operation)) {
|
||||
$codes = is_array($codes) ? $codes : [$codes];
|
||||
$codesStr = implode($operation === 'and' ? ',' : '或', $codes);
|
||||
throw new ForbiddenHttpException("缺少权限: {$codesStr}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use OpenApi\Annotations as OA;
|
||||
use app\controller\OpenApiController;
|
||||
use Webman\Route;
|
||||
use WebmanTech\Swagger\DTO\ConfigOpenapiDocDTO;
|
||||
use WebmanTech\Swagger\Integrations\RouteRegister;
|
||||
use WebmanTech\Swagger\RouteAnnotation\Reader;
|
||||
use app\adminapi\controller\terminal\TerminalSseController;
|
||||
|
||||
/**
|
||||
* 注册admin APP路由
|
||||
*/
|
||||
Route::group('/adminapi', function () {
|
||||
$openapiDocConfig = [
|
||||
'format' => 'json',
|
||||
'scan_path' => [
|
||||
base_path('app/schema'), // 基础 schema
|
||||
base_path('app/adminapi'),
|
||||
base_path('app/install'),
|
||||
],
|
||||
'modify' => function (OA\OpenApi $openapi) {
|
||||
$openapi->info->title = config('app.name') . ' API';
|
||||
$openapi->info->version = '1.0.0';
|
||||
$openapi->servers = [
|
||||
new OA\Server([
|
||||
'url' => '/adminapi',
|
||||
'description' => request()->host(),
|
||||
]),
|
||||
];
|
||||
/** @phpstan-ignore-next-line */
|
||||
if (!$openapi->components instanceof OA\Components) {
|
||||
$openapi->components = new OA\Components([]);
|
||||
}
|
||||
$openapi->components->securitySchemes = [
|
||||
new OA\SecurityScheme([
|
||||
'securityScheme' => 'api_key',
|
||||
'type' => 'apiKey',
|
||||
'name' => config('madong.jwt.app.token_name', 'Authorization'),
|
||||
'in' => 'header',
|
||||
]),
|
||||
];
|
||||
},
|
||||
];
|
||||
|
||||
$openApiController = new OpenApiController();
|
||||
Route::get('/openapi', static fn () => $openApiController->index('/adminapi/openapi.json'));
|
||||
Route::get('/openapi.json', static fn () => $openApiController->document($openapiDocConfig));
|
||||
|
||||
$config = ConfigOpenapiDocDTO::fromConfig($openapiDocConfig);
|
||||
RouteRegister::create()->register((new Reader())->getData($config->getScanSources()));
|
||||
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller;
|
||||
|
||||
use core\base\BaseController;
|
||||
|
||||
/**
|
||||
* 基类控制器继承的类
|
||||
*
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class Base extends BaseController
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
protected function initialize(): void
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,311 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\crontab;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\crontab\CrontabFormRequest;
|
||||
use app\adminapi\schema\request\crontab\CrontabQueryRequest;
|
||||
use app\adminapi\schema\response\crontab\CrontabResponse;
|
||||
use app\adminapi\validate\crontab\CrontabValidate;
|
||||
use app\enum\system\OperationResult;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\crontab\CrontabService;
|
||||
use core\exception\handler\AdminException;
|
||||
use core\tool\Json;
|
||||
use madong\helper\Arr;
|
||||
use madong\swagger\annotation\response\DataResponse;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class CrontabController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(CrontabService $service, CrontabValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/crontab/task',
|
||||
summary: '列表',
|
||||
tags: ['定时任务'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => CrontabQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'crontab:task:list')]
|
||||
#[PageResponse(CrontabResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/crontab/task/{id}',
|
||||
summary: '详情',
|
||||
tags: ['定时任务'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'crontab:task:read')]
|
||||
#[DataResponse(schema: CrontabResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $this->service->get($id);
|
||||
if (empty($data)) {
|
||||
throw new AdminException('数据未找到', ['errorCode' => 400]);
|
||||
}
|
||||
|
||||
$dataArray = $data->toArray();
|
||||
if (!empty($dataArray['cycle_rule'])) {
|
||||
$cycleRule = $dataArray['cycle_rule'];
|
||||
if (is_array($cycleRule)) {
|
||||
$cycleRule = array_map(function ($value) {
|
||||
if (is_string($value) && $value !== '' && is_numeric($value)) {
|
||||
return intval($value);
|
||||
}
|
||||
return $value;
|
||||
}, $cycleRule);
|
||||
$dataArray = array_merge($dataArray, $cycleRule);
|
||||
}
|
||||
unset($dataArray['cycle_rule']);
|
||||
}
|
||||
|
||||
return Json::success('ok', $dataArray);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/crontab/task',
|
||||
summary: '添加',
|
||||
tags: ['定时任务'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => CrontabFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'crontab:task:create')]
|
||||
#[DataResponse(schema: CrontabResponse::class, example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->inputFilter($request->all(), ['minute', 'hour', 'day', 'week', 'month', 'second']);
|
||||
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
if (!$this->validate->scene('store')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
$model = $this->service->save($data);
|
||||
if (empty($model)) {
|
||||
throw new AdminException('插入失败');
|
||||
}
|
||||
$pk = $model->getPk();
|
||||
return Json::success('ok', [$pk => $model->getData($pk)]);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/crontab/task/{id}',
|
||||
summary: '删除',
|
||||
tags: ['定时任务'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: '任务ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: 1)
|
||||
)]
|
||||
#[Permission(code: 'crontab:task:delete')]
|
||||
#[DataResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $request->input('data', []);
|
||||
$data = !empty($id) && $id !== '0' ? $id : $data;
|
||||
if (empty($data)) {
|
||||
throw new AdminException('参数错误');
|
||||
}
|
||||
$this->service->destroy($data);
|
||||
return Json::success('操作成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/crontab/task',
|
||||
summary: '批量删除',
|
||||
tags: ['定时任务'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'crontab:task:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->input('ids', []);
|
||||
if (empty($data)) {
|
||||
throw new AdminException('参数错误');
|
||||
}
|
||||
$this->service->destroy($data);
|
||||
return Json::success('操作成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/crontab/task/{id}',
|
||||
summary: '更新',
|
||||
tags: ['定时任务'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => CrontabFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: '任务ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: 1)
|
||||
)]
|
||||
#[Permission(code: 'crontab:task:update')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $this->inputFilter($request->all(), ['minute', 'hour', 'day', 'week', 'month', 'second']);
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
if (!$this->validate->scene('update')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
$this->service->update($id, $data);
|
||||
return Json::success('ok', []);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/crontab/task/{id}/resume',
|
||||
summary: '恢复',
|
||||
tags: ['定时任务'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: '任务ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: 1)
|
||||
)]
|
||||
#[Permission(code: 'crontab:task:resume')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function resume(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$this->service->resumeCrontab(Arr::normalize($id));
|
||||
return Json::success('执行完成');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/crontab/task/{id}/pause',
|
||||
summary: '暂停',
|
||||
tags: ['定时任务'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: '任务ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: 1)
|
||||
)]
|
||||
#[Permission(code: 'crontab:task:pause')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function pause(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$this->service->pauseCrontab($id);
|
||||
return Json::success('执行完成');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/crontab/task/{id}/execute',
|
||||
summary: '执行',
|
||||
tags: ['定时任务'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'crontab:task:execute')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function execute(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$result = $this->service->runOneTask(['id' => $id]);
|
||||
if ($result['code'] == OperationResult::FAILURE->value) {
|
||||
throw new AdminException('执行失败' . $result['log']);
|
||||
}
|
||||
return Json::success('执行完成', $result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage(), []);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\crontab;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\crontab\CrontabLogQueryRequest;
|
||||
use app\adminapi\schema\response\crontab\CrontabLogResponse;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\crontab\CrontabLogService;
|
||||
use madong\swagger\annotation\response\DataResponse;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class CrontabLogController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(CrontabLogService $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/crontab/log',
|
||||
summary: '列表',
|
||||
tags: ['定时任务.日志'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => CrontabLogQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'crontab:log:list')]
|
||||
#[PageResponse(schema: CrontabLogResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/crontab/log/{id}',
|
||||
summary: '详情',
|
||||
tags: ['定时任务.日志'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'crontab:log:read')]
|
||||
#[DataResponse(schema: CrontabLogResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/crontab/log/{id}',
|
||||
summary: '删除',
|
||||
tags: ['定时任务.日志'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'crontab:log:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/crontab/log',
|
||||
summary: '批量删除',
|
||||
tags: ['定时任务.日志'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'crontab:log:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\dict;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\dict\DictFormRequest;
|
||||
use app\adminapi\schema\request\dict\DictQueryRequest;
|
||||
use app\adminapi\schema\response\system\DictResponse;
|
||||
use app\adminapi\validate\system\DictValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\dict\DictService;
|
||||
use app\service\core\enum\EnumService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class DictController extends Crud
|
||||
{
|
||||
public function __construct(DictService $service, DictValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/dict/dict',
|
||||
summary: '列表',
|
||||
tags: ['字典管理'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => DictQueryRequest::class]
|
||||
)]
|
||||
#[Permission(code: 'dict:dict:list')]
|
||||
#[PageResponse(schema: DictResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/dict/dict',
|
||||
summary: '新增',
|
||||
tags: ['字典管理'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => DictFormRequest::class]
|
||||
)]
|
||||
#[Permission(code: 'dict:dict:create')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
return parent::store($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/dict/dict/{id}',
|
||||
summary: '更新',
|
||||
tags: ['字典管理'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => DictFormRequest::class]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: '字典ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'integer', default: 0),
|
||||
)]
|
||||
#[Permission(code: 'dict:dict:update')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
return parent::update($request);
|
||||
}
|
||||
#[OA\Post(
|
||||
path: '/dict/dict/export',
|
||||
summary: '导出',
|
||||
tags: ['字典管理']
|
||||
)]
|
||||
#[Permission(code: 'dict:dict:list', description: '导出')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function export(Request $request): \support\Response
|
||||
{
|
||||
return parent::export($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/dict/dict/{id}',
|
||||
summary: '删除',
|
||||
tags: ['字典管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'dict:dict:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/dict/dict',
|
||||
summary: '批量删除',
|
||||
tags: ['字典管理'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class]
|
||||
)]
|
||||
#[Permission(code: 'dict:dict:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/dict/dict/{id}',
|
||||
summary: '详情',
|
||||
tags: ['字典管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'dict:dict:read')]
|
||||
#[SimpleResponse(schema: DictResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/dict/dict/options/by-type',
|
||||
summary: '根据字典类型获取字典项',
|
||||
tags: ['字典管理'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'dict_type',
|
||||
description: '字典类型',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', default: 0),
|
||||
)]
|
||||
#[Permission(code: 'dict:dict:options')]
|
||||
#[SimpleResponse(example: '{"code": 0,"msg": "ok","data": [{"label": "是","value": 1,"color": "#4CAF50","ext": []},{"label": "否","value": 0,"color": "#FF5252","ext": []}]}')]
|
||||
public function getByDictType(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$dictType = $request->input('dict_type');
|
||||
$service = Container::make(EnumService::class);
|
||||
$data = $service->getEnumByCode($dictType);
|
||||
if (empty($data)) {
|
||||
$data = $this->service->findItemsByCode($dictType);
|
||||
}
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/dict/dict/enum/list',
|
||||
summary: '枚举字典列表',
|
||||
tags: ['字典管理'],
|
||||
)]
|
||||
#[Permission(code: 'dict:dict:enum_list')]
|
||||
#[SimpleResponse(schema: [], example: [
|
||||
'name' => 'CategoryEnum',
|
||||
'namespace' => 'app\\enum\\common\\CategoryEnum',
|
||||
'category' => 'common',
|
||||
'category_label' => '通用枚举',
|
||||
'code' => 'common.categoryenum',
|
||||
'options' => [
|
||||
[
|
||||
'label' => '通用枚举',
|
||||
'value' => 'common',
|
||||
'color' => 'blue',
|
||||
'ext' => [],
|
||||
],
|
||||
[
|
||||
'label' => '系统枚举',
|
||||
'value' => 'system',
|
||||
'color' => 'green',
|
||||
'ext' => [],
|
||||
],
|
||||
[
|
||||
'label' => '开发枚举',
|
||||
'value' => 'dev',
|
||||
'color' => 'orange',
|
||||
'ext' => [],
|
||||
],
|
||||
[
|
||||
'label' => '监控枚举',
|
||||
'value' => 'monitor',
|
||||
'color' => 'red',
|
||||
'ext' => [],
|
||||
],
|
||||
[
|
||||
'label' => '业务枚举',
|
||||
'value' => 'business',
|
||||
'color' => 'purple',
|
||||
'ext' => [],
|
||||
],
|
||||
],
|
||||
'count' => 5,
|
||||
])]
|
||||
public function enumDictList(Request $request): \support\Response
|
||||
{
|
||||
$page = $request->input('page', 1);
|
||||
$limit = $request->input('limit', 10);
|
||||
$search = $request->input('search', '');
|
||||
$category = $request->input('category', '');
|
||||
$service = Container::make(EnumService::class);
|
||||
$result = $service->getEnumsWithPagination($page, $limit, $search, $category);
|
||||
return Json::success('ok', $result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\dict;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\dict\DictItemFormRequest;
|
||||
use app\adminapi\schema\request\dict\DictItemQueryRequest;
|
||||
use app\adminapi\schema\response\system\DictItemResponse;
|
||||
use app\adminapi\validate\system\DictItemValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\dict\DictItemService;
|
||||
use madong\swagger\annotation\response\DataResponse;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class DictItemController extends Crud
|
||||
{
|
||||
public function __construct(DictItemService $service, DictItemValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/dict/item',
|
||||
summary: '列表',
|
||||
tags: ['字典项管理'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => DictItemQueryRequest::class]
|
||||
)]
|
||||
#[Permission(code: 'dict:item:list')]
|
||||
#[PageResponse(schema: DictItemResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/dict/item/{id}',
|
||||
summary: '详情',
|
||||
tags: ['字典项管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'dict:item:read')]
|
||||
#[DataResponse(schema: DictItemResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/dict/item',
|
||||
summary: '新增',
|
||||
tags: ['字典项管理'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => DictItemFormRequest::class]
|
||||
)]
|
||||
#[Permission(code: 'dict:item:create')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
return parent::store($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/dict/item/{id}',
|
||||
summary: '更新',
|
||||
tags: ['字典项管理'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => DictItemFormRequest::class]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: '字典项ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: '1')
|
||||
)]
|
||||
#[Permission(code: 'dict:item:update')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
return parent::update($request);
|
||||
}
|
||||
#[OA\Post(
|
||||
path: '/dict/item/export',
|
||||
summary: '导出',
|
||||
tags: ['字典项管理']
|
||||
)]
|
||||
#[Permission(code: 'dict:item:list', description: '导出')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function export(Request $request): \support\Response
|
||||
{
|
||||
return parent::export($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/dict/item/{id}',
|
||||
summary: '删除',
|
||||
tags: ['字典项管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: '字典项ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', default: '0', example: 0)
|
||||
)]
|
||||
#[Permission(code: 'dict:item:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/dict/item',
|
||||
summary: '批量删除',
|
||||
tags: ['字典项管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'dict:item:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\gateway;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\gateway\RateLimiterFormRequest;
|
||||
use app\adminapi\schema\request\gateway\RateLimiterQueryRequest;
|
||||
use app\adminapi\schema\response\gateway\RateLimiterResponse;
|
||||
use app\adminapi\validate\gateway\RateLimiterValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\schema\response\Result;
|
||||
use app\service\admin\gateway\RetaLimiterService;
|
||||
use core\exception\handler\AdminException;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\DataResponse;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class RateLimiterController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(RetaLimiterService $service, RateLimiterValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/gateway/limiter',
|
||||
summary: '列表',
|
||||
tags: ['限访规则'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => RateLimiterQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'gateway:limiter:list')]
|
||||
#[PageResponse(schema: RateLimiterResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/gateway/limiter/{id}',
|
||||
summary: '详情',
|
||||
tags: ['限访规则'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'gateway:limiter:read')]
|
||||
#[DataResponse(schema: RateLimiterResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/gateway/limiter',
|
||||
summary: '保存',
|
||||
tags: ['限访规则'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => RateLimiterFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'gateway:limiter:create')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->insertInput($request);
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
if (!$this->validate->scene('store')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
//这里对入库的start_date end_date 转换时间戳 合并到start_time end_time
|
||||
if (isset($data['start_date']) && !empty($data['start_date'])) {
|
||||
$data['start_time'] = strtotime($data['start_date']);
|
||||
unset($data['start_date']);
|
||||
}
|
||||
if (isset($data['end_date']) && !empty($data['end_date'])) {
|
||||
$data['end_time'] = strtotime($data['end_date']);
|
||||
unset($data['end_date']);
|
||||
}
|
||||
|
||||
$model = $this->service->save($data);
|
||||
if (empty($model)) {
|
||||
throw new AdminException('插入失败');
|
||||
}
|
||||
$pk = $model->getPk();
|
||||
//添加后移除缓存
|
||||
$this->service->cacheDriver()->delete(RetaLimiterService::CACHE_KEY);
|
||||
return Json::success('ok', [$pk => $model->getData($pk)]);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/gateway/limiter/{id}/change-status',
|
||||
summary: '更新状态',
|
||||
tags: ['限访规则'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'gateway:limiter:status')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function changeStatus(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->insertInput($request);
|
||||
$model = $this->service->getModel();
|
||||
$primaryKey = $model->getKeyName();
|
||||
if (!array_key_exists($primaryKey, $data)) {
|
||||
throw new \Exception('参数异常缺少主键');
|
||||
}
|
||||
$targetModel = $model->findOrFail($data[$primaryKey]);
|
||||
if (empty($targetModel)) {
|
||||
throw new \Exception('资源不存在' . $primaryKey . '=', $data[$primaryKey]);
|
||||
}
|
||||
$targetModel->fill($data);
|
||||
if (!$targetModel->save()) {
|
||||
throw new \RuntimeException('数据保存失败');
|
||||
}
|
||||
//清空缓存
|
||||
$this->service->cacheDriver()->delete(RetaLimiterService::CACHE_KEY);
|
||||
return Json::success('ok', []);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/gateway/limiter/{id}',
|
||||
summary: '更新',
|
||||
tags: ['限访规则'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => RateLimiterFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: '限访规则ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: '247298887944531968')
|
||||
)]
|
||||
#[Permission(code: 'gateway:limiter:update')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->insertInput($request);
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
if (!$this->validate->scene('update')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
//这里对入库的start_date end_date 转换时间戳 合并到start_time end_time
|
||||
if (isset($data['start_date']) && !empty($data['start_date'])) {
|
||||
$data['start_time'] = strtotime($data['start_date']);
|
||||
unset($data['start_date']);
|
||||
}
|
||||
if (isset($data['end_date']) && !empty($data['end_date'])) {
|
||||
$data['end_time'] = strtotime($data['end_date']);
|
||||
unset($data['end_date']);
|
||||
}
|
||||
$this->service->update($data['id'], $data);
|
||||
//清空缓存
|
||||
$this->service->cacheDriver()->delete(RetaLimiterService::CACHE_KEY);
|
||||
return Json::success('ok', []);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/gateway/limiter/{id}',
|
||||
summary: '删除',
|
||||
tags: ['限访规则'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'gateway:limiter:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->getDeleteIds($request);
|
||||
if (empty($data)) {
|
||||
throw new AdminException('参数错误');
|
||||
}
|
||||
$result = $this->service->transaction(function () use ($data) {
|
||||
$data = is_array($data) ? $data : explode(',', $data);
|
||||
$deletedIds = [];
|
||||
foreach ($data as $id) {
|
||||
$item = $this->service->get($id);
|
||||
if (!$item) {
|
||||
continue; // 如果找不到项,跳过
|
||||
}
|
||||
$item->delete();
|
||||
$primaryKey = $item->getPk();
|
||||
$deletedIds[] = $item->{$primaryKey};
|
||||
}
|
||||
return $deletedIds;
|
||||
});
|
||||
//清空缓存
|
||||
$this->service->cacheDriver()->delete(RetaLimiterService::CACHE_KEY);
|
||||
return Json::success('ok', $result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/gateway/limiter',
|
||||
summary: '批量删除',
|
||||
tags: ['限访规则'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'gateway:limiter:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->getDeleteIds($request);
|
||||
if (empty($data)) {
|
||||
throw new AdminException('参数错误');
|
||||
}
|
||||
$result = $this->service->transaction(function () use ($data) {
|
||||
$data = is_array($data) ? $data : explode(',', $data);
|
||||
$deletedIds = [];
|
||||
foreach ($data as $id) {
|
||||
$item = $this->service->get($id);
|
||||
if (!$item) {
|
||||
continue; // 如果找不到项,跳过
|
||||
}
|
||||
$item->delete();
|
||||
$primaryKey = $item->getPk();
|
||||
$deletedIds[] = $item->{$primaryKey};
|
||||
}
|
||||
return $deletedIds;
|
||||
});
|
||||
//清空缓存
|
||||
$this->service->cacheDriver()->delete(RetaLimiterService::CACHE_KEY);
|
||||
return Json::success('ok', $result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\gateway;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\gateway\RateRestrictionsFormRequest;
|
||||
use app\adminapi\schema\request\gateway\RateRestrictionsQueryRequest;
|
||||
use app\adminapi\schema\response\gateway\RateRestrictionsResponse;
|
||||
use app\adminapi\validate\gateway\RateRestrictionsValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\ChangeStatusRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\gateway\RetaRestrictionsService;
|
||||
use core\exception\handler\AdminException;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class RateRestrictionsController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(RetaRestrictionsService $service, RateRestrictionsValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/gateway/blacklist',
|
||||
summary: '列表',
|
||||
tags: ['限访名单'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => RateRestrictionsQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'gateway:blacklist:list')]
|
||||
#[PageResponse(schema: RateRestrictionsResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request); // TODO: Change the autogenerated stub
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/gateway/blacklist/{id}',
|
||||
summary: '详情',
|
||||
tags: ['限访名单'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'gateway:blacklist:read')]
|
||||
#[SimpleResponse(schema: RateRestrictionsResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/gateway/blacklist',
|
||||
summary: '保存',
|
||||
tags: ['限访名单'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => RateRestrictionsFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'gateway:blacklist:create')]
|
||||
#[SimpleResponse(schema: RateRestrictionsResponse::class, example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->insertInput($request);
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
if (!$this->validate->scene('store')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['start_date']) && !empty($data['start_date'])) {
|
||||
$data['start_time'] = strtotime($data['start_date']);
|
||||
unset($data['start_date']);
|
||||
}
|
||||
if (isset($data['end_date']) && !empty($data['end_date'])) {
|
||||
$data['end_time'] = strtotime($data['end_date']);
|
||||
unset($data['end_date']);
|
||||
}
|
||||
$model = $this->service->save($data);
|
||||
if (empty($model)) {
|
||||
throw new AdminException('插入失败');
|
||||
}
|
||||
$pk = $model->getPk();
|
||||
//添加后移除缓存
|
||||
$this->service->cacheDriver()->delete(RetaRestrictionsService::CACHE_KEY);
|
||||
return Json::success('ok', [$pk => $model->getData($pk)]);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/gateway/blacklist/{id}/status',
|
||||
summary: '切换状态',
|
||||
tags: ['限访名单'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'gateway:blacklist:status')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function changeStatus(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->insertInput($request);
|
||||
$model = $this->service->getModel();
|
||||
$primaryKey = $model->getKeyName();
|
||||
if (!array_key_exists($primaryKey, $data)) {
|
||||
throw new \Exception('参数异常缺少主键');
|
||||
}
|
||||
$targetModel = $model->findOrFail($data[$primaryKey]);
|
||||
if (empty($targetModel)) {
|
||||
throw new \Exception('资源不存在' . $primaryKey . '=', $data[$primaryKey]);
|
||||
}
|
||||
$targetModel->fill($data);
|
||||
if (!$targetModel->save()) {
|
||||
throw new \RuntimeException('数据保存失败');
|
||||
}
|
||||
//清空缓存
|
||||
$this->service->cacheDriver()->delete(RetaRestrictionsService::CACHE_KEY);
|
||||
return Json::success('ok', []);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/gateway/blacklist/{id}',
|
||||
summary: '更新',
|
||||
tags: ['限访名单'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => RateRestrictionsFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: 'ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string'))]
|
||||
#[Permission(code: 'gateway:blacklist:update')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->insertInput($request);
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
if (!$this->validate->scene('update')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
$this->service->update($data['id'], $data);
|
||||
//清空缓存
|
||||
$this->service->cacheDriver()->delete(RetaRestrictionsService::CACHE_KEY);
|
||||
return Json::success('ok', []);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/gateway/blacklist/{id}',
|
||||
summary: '删除',
|
||||
tags: ['限访名单'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'gateway:blacklist:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id'); // 获取路由地址 id从
|
||||
$data = $request->input('data', []);
|
||||
$data = !empty($id) && $id !== '0' ? $id : $data;
|
||||
if (empty($data)) {
|
||||
throw new AdminException('参数错误');
|
||||
}
|
||||
$result = $this->service->transaction(function () use ($data) {
|
||||
$data = is_array($data) ? $data : explode(',', $data);
|
||||
$deletedIds = [];
|
||||
foreach ($data as $id) {
|
||||
$item = $this->service->get($id);
|
||||
if (!$item) {
|
||||
continue; // 如果找不到项,跳过
|
||||
}
|
||||
$item->delete();
|
||||
$primaryKey = $item->getPk();
|
||||
$deletedIds[] = $item->{$primaryKey};
|
||||
}
|
||||
return $deletedIds;
|
||||
});
|
||||
//清空缓存
|
||||
$this->service->cacheDriver()->delete(RetaRestrictionsService::CACHE_KEY);
|
||||
return Json::success('ok', $result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/gateway/blacklist',
|
||||
summary: '批量删除',
|
||||
tags: ['限访名单'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'gateway:blacklist:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
|
||||
$data = $request->input('ids', []);
|
||||
if (empty($data)) {
|
||||
throw new AdminException('参数错误');
|
||||
}
|
||||
$result = $this->service->transaction(function () use ($data) {
|
||||
$data = is_array($data) ? $data : explode(',', $data);
|
||||
$deletedIds = [];
|
||||
foreach ($data as $id) {
|
||||
$item = $this->service->get($id);
|
||||
if (!$item) {
|
||||
continue; // 如果找不到项,跳过
|
||||
}
|
||||
$item->delete();
|
||||
$primaryKey = $item->getPk();
|
||||
$deletedIds[] = $item->{$primaryKey};
|
||||
}
|
||||
return $deletedIds;
|
||||
});
|
||||
//清空缓存
|
||||
$this->service->cacheDriver()->delete(RetaRestrictionsService::CACHE_KEY);
|
||||
return Json::success('ok', $result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\generator;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\validate\generator\GeneratorValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\generator\GeneratorService;
|
||||
use app\service\admin\generator\GeneratorTableService;
|
||||
use madong\swagger\annotation\response\DataResponse;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use core\exception\handler\AdminException;
|
||||
use core\tool\Json;
|
||||
use madong\helper\Arr;
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class GeneratorController extends Crud
|
||||
{
|
||||
public function __construct(GeneratorService $service, GeneratorValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/generator/code',
|
||||
summary: '列表',
|
||||
tags: ['代码生成器']
|
||||
)]
|
||||
#[Permission(code: 'generator:code:list')]
|
||||
#[PageResponse(schema: [], example: [[
|
||||
'id' => '273048217217138688',
|
||||
'table_name' => 'sys_admin_post',
|
||||
'table_content' => '用户与岗位关联表',
|
||||
'module_name' => 'sys_admin_post',
|
||||
'class_name' => 'sys_admin_post',
|
||||
'edit_type' => 1,
|
||||
'plugin_name' => '',
|
||||
'order_type' => 0,
|
||||
'parent_menu' => '',
|
||||
'relations' => null,
|
||||
'push_sync_count' => 0,
|
||||
'created_at' => '2026-01-23T03:16:07.000000Z',
|
||||
'updated_at' => '2026-01-23T03:16:07.000000Z',
|
||||
'created_date' => '2026-01-23 11:16:07',
|
||||
'updated_date' => '2026-01-23 11:16:07',
|
||||
]])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request); // TODO: Change the autogenerated stub
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/generator/code/table-list',
|
||||
summary: '数据库表-列表',
|
||||
tags: ['代码生成器']
|
||||
)]
|
||||
#[Permission(code: 'generator:code:list')]
|
||||
#[DataResponse(schema: [], example: [])]
|
||||
public function tableList(Request $request): \support\Response
|
||||
{
|
||||
$data = $request->all();
|
||||
$page = $request->input('page', 1);
|
||||
$limit = $request->input('limit', 10);
|
||||
$result = $this->service->tableList($data, (int)$page, (int)$limit);
|
||||
return Json::success($result);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/generator/code',
|
||||
summary: '生成代码',
|
||||
tags: ['代码生成器']
|
||||
)]
|
||||
#[Permission(code: 'generator:code:create')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->input('table', []);
|
||||
foreach ($data as $item) {
|
||||
if (!$this->validate->scene('store')->check($item)) {
|
||||
return Json::fail($this->validate->getError());
|
||||
}
|
||||
}
|
||||
foreach ($data as $item) {
|
||||
$this->service->store($item);
|
||||
}
|
||||
return Json::success();
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/generator/code/{id}',
|
||||
summary: '删除',
|
||||
tags: ['代码生成器'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'generator:code:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$this->service->destroy(Arr::normalize($id));
|
||||
return Json::success();
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/generator/code',
|
||||
summary: '批量删除',
|
||||
tags: ['代码生成器'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'generator:code:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function remove(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->input('ids', []);
|
||||
$this->service->destroy(Arr::normalize($data));
|
||||
return Json::success();
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/generator/code/{id}',
|
||||
summary: '详情',
|
||||
tags: ['代码生成器'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'generator:code:read')]
|
||||
#[DataResponse(schema: [], example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$service = Container::make(GeneratorTableService::class);
|
||||
$data = $service->getDetail($id);
|
||||
if (empty($data)) {
|
||||
throw new AdminException('数据未找到', ['errorCode' => 400]);
|
||||
}
|
||||
return Json::success('ok', $data->toArray());
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/generator/code/{id}',
|
||||
summary: '更新',
|
||||
tags: ['代码生成器'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'generator:code:update')]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $request->all();
|
||||
if (!$this->validate->scene('update')->check($data)) {
|
||||
return Json::fail($this->validate->getError());
|
||||
}
|
||||
$this->service->update($id, $data);
|
||||
return Json::success();
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/generator/code/{id}/preview',
|
||||
summary: '预览',
|
||||
tags: ['代码生成器'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'generator:code:preview')]
|
||||
public function preview(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$result = $this->service->preview(['id' => $id]);
|
||||
return Json::success($result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/generator/code/{id}/download',
|
||||
summary: '下载',
|
||||
tags: ['代码生成器'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'generator:code:download')]
|
||||
public function download(Request $request)
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$result = $this->service->download($id);
|
||||
return response()->download($result['file_path'], $result['file_name']);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/generator/code/{id}/deploy',
|
||||
summary: '部署',
|
||||
tags: ['代码生成器'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'generator:code:deploy')]
|
||||
public function deploy(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$result = $this->service->deploy(['id' => $id]);
|
||||
return Json::success($result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\lang;
|
||||
|
||||
use app\adminapi\controller\Base;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\service\core\lang\TranslationService;
|
||||
use core\exception\handler\AdminException;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, OperationMiddleware::class)]
|
||||
final class LangController extends Base
|
||||
{
|
||||
public function __construct(TranslationService $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/lang/supported',
|
||||
summary: '获取支持的语言列表',
|
||||
tags: ['语言'],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: '成功',
|
||||
content: new OA\JsonContent(
|
||||
type: 'array',
|
||||
items: new OA\Items(type: 'string')
|
||||
)
|
||||
)
|
||||
]
|
||||
)]
|
||||
#[SimpleResponse(example: ['code' => 0,'message' => 'success','data' => []])]
|
||||
public function supported(Request $request): Response
|
||||
{
|
||||
try {
|
||||
$languages = $this->service->getSupportedLanguages();
|
||||
return json(['code' => 200, 'msg' => 'success', 'data' => $languages]);
|
||||
} catch (\Exception $e) {
|
||||
throw new AdminException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/lang',
|
||||
summary: '获取语言包列表',
|
||||
tags: ['语言'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'language',
|
||||
description: '语言类型(如en|zh_CN等)',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string', example: 'zh_CN')
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'file',
|
||||
description: '翻译文件名称',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string', example: 'messages')
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'keyword',
|
||||
description: '搜索关键词(支持key和翻译内容)',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'page',
|
||||
description: '页码',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', default: 1)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'limit',
|
||||
description: '每页数量',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', default: 10)
|
||||
)
|
||||
]
|
||||
)]
|
||||
#[PageResponse(schema: [], example: [])]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
try {
|
||||
$language = $request->get('language');
|
||||
$file = $request->get('file');
|
||||
$keyword = $request->get('keyword');
|
||||
$page = (int)$request->get('page', 1);
|
||||
$limit = (int)$request->get('limit', 10);
|
||||
|
||||
$result = $this->service->getList($language, $file, $keyword, $page, $limit);
|
||||
return json(['code' => 200, 'msg' => 'success', 'data' => $result]);
|
||||
} catch (\Exception $e) {
|
||||
throw new AdminException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/lang/files',
|
||||
summary: '获取翻译文件列表',
|
||||
tags: ['语言'],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: '成功',
|
||||
content: new OA\JsonContent(
|
||||
type: 'array',
|
||||
items: new OA\Items(type: 'string')
|
||||
)
|
||||
)
|
||||
]
|
||||
)]
|
||||
#[SimpleResponse(schema:[],example: [])]
|
||||
public function files(Request $request): Response
|
||||
{
|
||||
try {
|
||||
$files = $this->service->getFileList();
|
||||
return json(['code' => 200, 'msg' => 'success', 'data' => $files]);
|
||||
} catch (\Exception $e) {
|
||||
throw new AdminException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/lang/statistics',
|
||||
summary: '获取语言包统计信息',
|
||||
tags: ['语言'],
|
||||
)]
|
||||
#[SimpleResponse(schema:[],example: [])]
|
||||
public function statistics(Request $request): Response
|
||||
{
|
||||
try {
|
||||
$stats = $this->service->getStatistics();
|
||||
return json(['code' => 200, 'msg' => 'success', 'data' => $stats]);
|
||||
} catch (\Exception $e) {
|
||||
throw new AdminException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \core\exception\handler\AdminException
|
||||
*/
|
||||
#[OA\Get(
|
||||
path: '/lang/translate',
|
||||
summary: '根据key获取翻译内容',
|
||||
tags: ['语言'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'key',
|
||||
description: '翻译key(格式:文件.键,如:common.operation.success)',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: 'common.operation.success')
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'language',
|
||||
description: '语言类型(如en|zh_CN等)',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string', example: 'zh_CN')
|
||||
)
|
||||
]
|
||||
)]
|
||||
#[SimpleResponse(schema:[],example: [])]
|
||||
public function translate(Request $request): Response
|
||||
{
|
||||
try {
|
||||
$key = $request->get('key');
|
||||
$language = $request->get('language');
|
||||
$parameters = $request->get('parameters', []);
|
||||
if(empty($language)){
|
||||
$language = locale();
|
||||
}
|
||||
|
||||
// 如果parameters是字符串,尝试解析为JSON
|
||||
if (is_string($parameters)) {
|
||||
$parameters = json_decode($parameters, true) ?: [];
|
||||
}
|
||||
|
||||
$translation = $this->service->getTranslationByKey($key, $language, $parameters);
|
||||
|
||||
if ($translation === null) {
|
||||
return json(['code' => 404, 'msg' => 'Translation key not found', 'data' => $key]);
|
||||
}
|
||||
|
||||
return Json::success('common.operation.success',$translation);
|
||||
} catch (\Exception $e) {
|
||||
throw new AdminException($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\login;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\schema\request\login\LoginRequest;
|
||||
use app\adminapi\schema\request\ThirdPartyLoginRequest;
|
||||
use app\service\admin\system\AdminService;
|
||||
use core\cache\CacheService;
|
||||
use core\captcha\Captcha;
|
||||
use core\exception\handler\AdminException;
|
||||
use core\jwt\JwtToken;
|
||||
use core\tool\Json;
|
||||
use core\tool\RSAService;
|
||||
use core\uuid\UUIDGenerator;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use OpenApi\Attributes as OA;
|
||||
use OpenApi\Attributes\RequestBody;
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
|
||||
final class LoginController extends Crud
|
||||
{
|
||||
public function __construct(AdminService $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/login/get-captcha-open-flag',
|
||||
summary: '验证码开启状态',
|
||||
tags: ['无需授权'],
|
||||
)]
|
||||
#[SimpleResponse(schema: ['flag' => true], example:[])]
|
||||
public function getCaptchaOpenFlag(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
// 生成密钥对
|
||||
$cache = Container::make(CacheService::class, []);
|
||||
$keyId = md5(UUIDGenerator::generate());
|
||||
$keys = RSAService::generateKeys();
|
||||
// 存储私钥到缓存,用于解密密码
|
||||
$cache->set("rsa_private_key_$keyId", $keys['private'], 60); // 5分钟过期
|
||||
return Json::success('ok', ['flag' => config('core.captcha.app.enable', false), 'key_id' => $keyId, 'public_key' => $keys['public']]);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/login/captcha',
|
||||
summary: '验证码',
|
||||
tags: ['无需授权'],
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example:[])]
|
||||
public function captcha(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$captcha = new Captcha();
|
||||
$type = $request->input('type', 'admin-login');
|
||||
$result = $captcha->captcha($request, $type);
|
||||
return Json::success('ok', $result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/login/send-sms',
|
||||
summary: '手机验证码',
|
||||
tags: ['无需授权']
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'mobile_phone',
|
||||
description: '手机号码',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'integer', default: 18888888888)
|
||||
)]
|
||||
#[SimpleResponse(schema:[],example: '{"code": 185841,"uid":18888888888}}')]
|
||||
public function sendSms(Request $request): \support\Response
|
||||
{
|
||||
// 1.0生成验证码
|
||||
$captcha = new Captcha();
|
||||
$result = $captcha->generateSmsCaptcha($request);
|
||||
// 2.0发送验证码到手机
|
||||
// 3.0 存储验证码到 Redis,设置过期时间(如 5 分钟)
|
||||
return Json::success('验证码发送成功', $result);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/login/login',
|
||||
summary: '登录',
|
||||
tags: ['无需授权']
|
||||
)]
|
||||
#[RequestBody(
|
||||
description: '请求参数',
|
||||
required: true,
|
||||
content: new OA\JsonContent(ref: LoginRequest::class)
|
||||
)]
|
||||
#[SimpleResponse(schema:[],example: '{"token_type": "Bearer","expires_in": 7200,"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtYWRvbmcudGVjaCIsImF1ZCI6Im1hZG9uZy50ZWNoIiwiaWF0IjoxNzYyNDExMjY5LCJuYmYiOjE3NjI0MTEyNjksImV4cCI6MTc2MjQxODQ2OSwiZXh0ZW5kIjp7ImlkIjoiMiIsInVzZXJfbmFtZSI6ImFkbWluIiwicmVhbF9uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4Iiwibmlja19uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4IiwiaXNfc3VwZXIiOjEsImlzX3RlbmFudF9hZG1pbiI6MSwibW9iaWxlX3Bob25lIjpudWxsLCJlbWFpbCI6bnVsbCwiYXZhdGFyIjoiL3VwbG9hZC80MmY4NmI5YjM2Nzk0ZmI5ZTM4MDkxN2MyNTFmNmQ4MS5wbmciLCJzaWduZWQiOm51bGwsImRhc2hib2FyZCI6bnVsbCwiZGVwdF9pZCI6bnVsbCwiZW5hYmxlZCI6MSwibG9naW5faXAiOiIxMjAuNDEuMjQ2LjIyNSIsImxvZ2luX3RpbWUiOjE3NjI0MTEyNjksInVwZGF0ZWRfYXQiOiIyMDI1LTExLTA2VDA2OjQxOjA5LjAwMDAwMFoiLCJzZXgiOjAsImJpcnRoZGF5IjpudWxsLCJ0ZWwiOm51bGwsImlzX2xvY2tlZCI6MCwiZGVwdHMiOltdLCJwb3N0cyI6W10sInRlbmFudHMiOlt7ImlkIjoiMSIsImRiX25hbWUiOiJtYWRvbmdfYWRtaW5fc2FhcyIsImNvZGUiOiJwbGF0Zm9ybSIsInR5cGUiOjAsImNvbnRhY3RfcGVyc29uIjoiYWRtaW4iLCJjb250YWN0X3Bob25lIjoiMTg4ODg4ODg4ODgiLCJjb21wYW55X25hbWUiOiJ4eHh4IFx1NjcwOVx1OTY1MFx1NTE2Y1x1NTNmOCIsImxpY2Vuc2VfbnVtYmVyIjoiIiwiYWRkcmVzcyI6Ilx1NGUyZFx1NTZmZCIsImRlc2NyaXB0aW9uIjoiXHU1MTg1XHU3ZjZlXHU4ZDI2XHU1M2Y3IiwiZG9tYWluIjoiaHR0cHM6Ly93d3cubWFkb25nLnRlY2giLCJlbmFibGVkIjoxLCJpc29sYXRpb25fbW9kZSI6MiwiaXNfZGVmYXVsdCI6MSwiZXhwaXJlZF9hdCI6bnVsbCwiZGVsZXRlZF9hdCI6bnVsbCwiY3JlYXRlZF9hdCI6IjIwMjUtMTAtMDNUMDM6MjM6NDcuMDAwMDAwWiIsImNyZWF0ZWRfYnkiOjEsInVwZGF0ZWRfYnkiOm51bGwsInVwZGF0ZWRfYXQiOm51bGwsImNyZWF0ZWRfZGF0ZSI6IjIwMjUtMTAtMDMgMTE6MjM6NDciLCJ1cGRhdGVkX2RhdGUiOm51bGwsImV4cGlyZWRfZGF0ZSI6bnVsbCwicGl2b3QiOnsiYWRtaW5faWQiOjIsInRlbmFudF9pZCI6MSwiY3JlYXRlZF9kYXRlIjpudWxsLCJ1cGRhdGVkX2RhdGUiOm51bGx9fV0sImNhc2JpbiI6W119fQ.rCaHoiI1gU7xyoJazt3V5JQrS2guV34PH-JjkWnceE4","refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtYWRvbmcudGVjaCIsImF1ZCI6Im1hZG9uZy50ZWNoIiwiaWF0IjoxNzYyNDExMjY5LCJuYmYiOjE3NjI0MTEyNjksImV4cCI6MTc2MzAxNjA2OSwiZXh0ZW5kIjp7ImlkIjoiMiIsInVzZXJfbmFtZSI6ImFkbWluIiwicmVhbF9uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4Iiwibmlja19uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4IiwiaXNfc3VwZXIiOjEsImlzX3RlbmFudF9hZG1pbiI6MSwibW9iaWxlX3Bob25lIjpudWxsLCJlbWFpbCI6bnVsbCwiYXZhdGFyIjoiL3VwbG9hZC80MmY4NmI5YjM2Nzk0ZmI5ZTM4MDkxN2MyNTFmNmQ4MS5wbmciLCJzaWduZWQiOm51bGwsImRhc2hib2FyZCI6bnVsbCwiZGVwdF9pZCI6bnVsbCwiZW5hYmxlZCI6MSwibG9naW5faXAiOiIxMjAuNDEuMjQ2LjIyNSIsImxvZ2luX3RpbWUiOjE3NjI0MTEyNjksInVwZGF0ZWRfYXQiOiIyMDI1LTExLTA2VDA2OjQxOjA5LjAwMDAwMFoiLCJzZXgiOjAsImJpcnRoZGF5IjpudWxsLCJ0ZWwiOm51bGwsImlzX2xvY2tlZCI6MCwiZGVwdHMiOltdLCJwb3N0cyI6W10sInRlbmFudHMiOlt7ImlkIjoiMSIsImRiX25hbWUiOiJtYWRvbmdfYWRtaW5fc2FhcyIsImNvZGUiOiJwbGF0Zm9ybSIsInR5cGUiOjAsImNvbnRhY3RfcGVyc29uIjoiYWRtaW4iLCJjb250YWN0X3Bob25lIjoiMTg4ODg4ODg4ODgiLCJjb21wYW55X25hbWUiOiJ4eHh4IFx1NjcwOVx1OTY1MFx1NTE2Y1x1NTNmOCIsImxpY2Vuc2VfbnVtYmVyIjoiIiwiYWRkcmVzcyI6Ilx1NGUyZFx1NTZmZCIsImRlc2NyaXB0aW9uIjoiXHU1MTg1XHU3ZjZlXHU4ZDI2XHU1M2Y3IiwiZG9tYWluIjoiaHR0cHM6Ly93d3cubWFkb25nLnRlY2giLCJlbmFibGVkIjoxLCJpc29sYXRpb25fbW9kZSI6MiwiaXNfZGVmYXVsdCI6MSwiZXhwaXJlZF9hdCI6bnVsbCwiZGVsZXRlZF9hdCI6bnVsbCwiY3JlYXRlZF9hdCI6IjIwMjUtMTAtMDNUMDM6MjM6NDcuMDAwMDAwWiIsImNyZWF0ZWRfYnkiOjEsInVwZGF0ZWRfYnkiOm51bGwsInVwZGF0ZWRfYXQiOm51bGwsImNyZWF0ZWRfZGF0ZSI6IjIwMjUtMTAtMDMgMTE6MjM6NDciLCJ1cGRhdGVkX2RhdGUiOm51bGwsImV4cGlyZWRfZGF0ZSI6bnVsbCwicGl2b3QiOnsiYWRtaW5faWQiOjIsInRlbmFudF9pZCI6MSwiY3JlYXRlZF9kYXRlIjpudWxsLCJ1cGRhdGVkX2RhdGUiOm51bGx9fV0sImNhc2JpbiI6W119fQ.IgLkKhjjsgjAsjS4qReg51WpzwjiuU27_tbqTpUuD3M","client_id": "127.0.0.1-690c430548ecd5.55813566","expires_time": 1762418469}')]
|
||||
public function login(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$username = $request->input('user_name');
|
||||
$password = $request->input('password', '');
|
||||
$phone = $request->input('mobile_phone', '');
|
||||
$code = $request->input('code', '');
|
||||
$uuid = $request->input('uuid', '');
|
||||
$type = $request->input('type', 'admin');
|
||||
$grantType = $request->input('grant_type', 'deafult');//refresh_token sms default 可以自行定义拓展登录方式
|
||||
$keyId = $request->input('key_id', '');//获取公钥Id
|
||||
|
||||
|
||||
$captcha = new Captcha();
|
||||
|
||||
if (config('core.captcha.app.enable') && $grantType === 'default') {
|
||||
if (!$captcha->check($uuid, $code)) {
|
||||
throw new AdminException('图片验证码错误!');
|
||||
}
|
||||
}
|
||||
|
||||
if ($grantType == 'sms' && !empty($phone)) {
|
||||
if (!$captcha->check($phone, $code)) {
|
||||
throw new AdminException('手机验证码错误!');
|
||||
}
|
||||
$info = $this->service->get(['mobile_phone' => $phone]);
|
||||
if (empty($info)) {
|
||||
throw new AdminException('当前手机未绑定用户!');
|
||||
}
|
||||
$username = $info->getData('user_name');
|
||||
}
|
||||
|
||||
$data = $this->service->login($username, $password, $type, $grantType, ['key_id' => $keyId ?? '']);
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/login/logout',
|
||||
summary: '注销',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['无需授权'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'token',
|
||||
description: '令牌(可选,也可通过Authorization头传递)',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[SimpleResponse(schema:[],example: [])]
|
||||
public function logout(Request $request): \support\Response
|
||||
{
|
||||
// 1. 获取token
|
||||
$token = $request->header(config('core.jwt.app.token_name', 'Authorization')) ?: $request->get('token');
|
||||
|
||||
// 2. 快速验证无效token情况
|
||||
if (empty($token) || $token === 'undefined') {
|
||||
return Json::success('ok', []);
|
||||
}
|
||||
|
||||
// 3. 处理Bearer Token格式
|
||||
if (str_starts_with($token, 'Bearer ')) {
|
||||
$token = substr($token, 7); // 移除"Bearer "前缀
|
||||
if (empty($token) || $token === 'undefined') {
|
||||
return Json::success('ok', []);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 退出登录
|
||||
(new JwtToken())->logout($token);
|
||||
|
||||
return Json::success('ok', []);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/login/auth/public-key',
|
||||
summary: '生成RSA密钥对',
|
||||
tags: ['无需授权'],
|
||||
)]
|
||||
#[SimpleResponse(schema:[],example: [])]
|
||||
public function generateRSAKeyPair(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
// 生成密钥对
|
||||
$cache = Container::make(CacheService::class, []);
|
||||
$keyId = md5(UUIDGenerator::generate());
|
||||
$keys = RSAService::generateKeys();
|
||||
// 存储私钥到缓存,用于解密密码
|
||||
$cache->set("rsa_private_key_$keyId", $keys['private'], 60); // 5分钟过期
|
||||
return Json::success('ok', ['public_key' => $keys['public'], 'key_id' => $keyId]);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/login/third-party/login',
|
||||
summary: '第三方测试登录接口(免RSA)',
|
||||
tags: ['无需授权']
|
||||
)]
|
||||
#[RequestBody(
|
||||
description: '请求参数',
|
||||
required: true,
|
||||
content: new OA\JsonContent(ref: ThirdPartyLoginRequest::class)
|
||||
)]
|
||||
#[SimpleResponse(schema:[],example: '{"code": 0,"msg": "ok","data": {"token_type": "Bearer","expires_in": 7200,"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtYWRvbmcudGVjaCIsImF1ZCI6Im1hZG9uZy50ZWNoIiwiaWF0IjoxNzYyNDExMjY5LCJuYmYiOjE3NjI0MTEyNjksImV4cCI6MTc2MjQxODQ2OSwiZXh0ZW5kIjp7ImlkIjoiMiIsInVzZXJfbmFtZSI6ImFkbWluIiwicmVhbF9uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4Iiwibmlja19uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4IiwiaXNfc3VwZXIiOjEsImlzX3RlbmFudF9hZG1pbiI6MSwibW9iaWxlX3Bob25lIjpudWxsLCJlbWFpbCI6bnVsbCwiYXZhdGFyIjoiL3VwbG9hZC80MmY4NmI5YjM2Nzk0ZmI5ZTM4MDkxN2MyNTFmNmQ4MS5wbmciLCJzaWduZWQiOm51bGwsImRhc2hib2FyZCI6bnVsbCwiZGVwdF9pZCI6bnVsbCwiZW5hYmxlZCI6MSwibG9naW5faXAiOiIxMjAuNDEuMjQ2LjIyNSIsImxvZ2luX3RpbWUiOjE3NjI0MTEyNjksInVwZGF0ZWRfYXQiOiIyMDI1LTExLTA2VDA2OjQxOjA5LjAwMDAwMFoiLCJzZXgiOjAsImJpcnRoZGF5IjpudWxsLCJ0ZWwiOm51bGwsImlzX2xvY2tlZCI6MCwiZGVwdHMiOltdLCJwb3N0cyI6W10sInRlbmFudHMiOlt7ImlkIjoiMSIsImRiX25hbWUiOiJtYWRvbmdfYWRtaW5fc2FhcyIsImNvZGUiOiJwbGF0Zm9ybSIsInR5cGUiOjAsImNvbnRhY3RfcGVyc29uIjoiYWRtaW4iLCJjb250YWN0X3Bob25lIjoiMTg4ODg4ODg4ODgiLCJjb21wYW55X25hbWUiOiJ4eHh4IFx1NjcwOVx1OTY1MFx1NTE2Y1x1NTNmOCIsImxpY2Vuc2VfbnVtYmVyIjoiIiwiYWRkcmVzcyI6Ilx1NGUyZFx1NTZmZCIsImRlc2NyaXB0aW9uIjoiXHU1MTg1XHU3ZjZlXHU4ZDI2XHU1M2Y3IiwiZG9tYWluIjoiaHR0cHM6Ly93d3cubWFkb25nLnRlY2giLCJlbmFibGVkIjoxLCJpc29sYXRpb25fbW9kZSI6MiwiaXNfZGVmYXVsdCI6MSwiZXhwaXJlZF9hdCI6bnVsbCwiZGVsZXRlZF9hdCI6bnVsbCwiY3JlYXRlZF9hdCI6IjIwMjUtMTAtMDNUMDM6MjM6NDcuMDAwMDAwWiIsImNyZWF0ZWRfYnkiOjEsInVwZGF0ZWRfYnkiOm51bGwsInVwZGF0ZWRfYXQiOm51bGwsImNyZWF0ZWRfZGF0ZSI6IjIwMjUtMTAtMDMgMTE6MjM6NDciLCJ1cGRhdGVkX2RhdGUiOm51bGwsImV4cGlyZWRfZGF0ZSI6bnVsbCwicGl2b3QiOnsiYWRtaW5faWQiOjIsInRlbmFudF9pZCI6MSwiY3JlYXRlZF9kYXRlIjpudWxsLCJ1cGRhdGVkX2RhdGUiOm51bGx9fV0sImNhc2JpbiI6W119fQ.rCaHoiI1gU7xyoJazt3V5JQrS2guV34PH-JjkWnceE4","refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtYWRvbmcudGVjaCIsImF1ZCI6Im1hZG9uZy50ZWNoIiwiaWF0IjoxNzYyNDExMjY5LCJuYmYiOjE3NjI0MTEyNjksImV4cCI6MTc2MzAxNjA2OSwiZXh0ZW5kIjp7ImlkIjoiMiIsInVzZXJfbmFtZSI6ImFkbWluIiwicmVhbF9uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4Iiwibmlja19uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4IiwiaXNfc3VwZXIiOjEsImlzX3RlbmFudF9hZG1pbiI6MSwibW9iaWxlX3Bob25lIjpudWxsLCJlbWFpbCI6bnVsbCwiYXZhdGFyIjoiL3VwbG9hZC80MmY4NmI5YjM2Nzk0ZmI5ZTM4MDkxN2MyNTFmNmQ4MS5wbmciLCJzaWduZWQiOm51bGwsImRhc2hib2FyZCI6bnVsbCwiZGVwdF9pZCI6bnVsbCwiZW5hYmxlZCI6MSwibG9naW5faXAiOiIxMjAuNDEuMjQ2LjIyNSIsImxvZ2luX3RpbWUiOjE3NjI0MTEyNjksInVwZGF0ZWRfYXQiOiIyMDI1LTExLTA2VDA2OjQxOjA5LjAwMDAwMFoiLCJzZXgiOjAsImJpcnRoZGF5IjpudWxsLCJ0ZWwiOm51bGwsImlzX2xvY2tlZCI6MCwiZGVwdHMiOltdLCJwb3N0cyI6W10sInRlbmFudHMiOlt7ImlkIjoiMSIsImRiX25hbWUiOiJtYWRvbmdfYWRtaW5fc2FhcyIsImNvZGUiOiJwbGF0Zm9ybSIsInR5cGUiOjAsImNvbnRhY3RfcGVyc29uIjoiYWRtaW4iLCJjb250YWN0X3Bob25lIjoiMTg4ODg4ODg4ODgiLCJjb21wYW55X25hbWUiOiJ4eHh4IFx1NjcwOVx1OTY1MFx1NTE2Y1x1NTNmOCIsImxpY2Vuc2VfbnVtYmVyIjoiIiwiYWRkcmVzcyI6Ilx1NGUyZFx1NTZmZCIsImRlc2NyaXB0aW9uIjoiXHU1MTg1XHU3ZjZlXHU4ZDI2XHU1M2Y3IiwiZG9tYWluIjoiaHR0cHM6Ly93d3cubWFkb25nLnRlY2giLCJlbmFibGVkIjoxLCJpc29sYXRpb25fbW9kZSI6MiwiaXNfZGVmYXVsdCI6MSwiZXhwaXJlZF9hdCI6bnVsbCwiZGVsZXRlZF9hdCI6bnVsbCwiY3JlYXRlZF9hdCI6IjIwMjUtMTAtMDNUMDM6MjM6NDcuMDAwMDAwWiIsImNyZWF0ZWRfYnkiOjEsInVwZGF0ZWRfYnkiOm51bGwsInVwZGF0ZWRfYXQiOm51bGwsImNyZWF0ZWRfZGF0ZSI6IjIwMjUtMTAtMDMgMTE6MjM6NDciLCJ1cGRhdGVkX2RhdGUiOm51bGwsImV4cGlyZWRfZGF0ZSI6bnVsbCwicGl2b3QiOnsiYWRtaW5faWQiOjIsInRlbmFudF9pZCI6MSwiY3JlYXRlZF9kYXRlIjpudWxsLCJ1cGRhdGVkX2RhdGUiOm51bGx9fV0sImNhc2JpbiI6W119fQ.IgLkKhjjsgjAsjS4qReg51WpzwjiuU27_tbqTpUuD3M","client_id": "127.0.0.1-690c430548ecd5.55813566","expires_time": 1762418469}}')]
|
||||
public function thirdPartyLogin(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$acctId = $request->get('acct_id');
|
||||
$appId = $request->get('app_id');
|
||||
$appSecret = $request->get('app_secret');
|
||||
$userName = $request->get('user_name');
|
||||
$data = $this->service->thirdPartyLogin($acctId, $appId, $appSecret, $userName);
|
||||
return Json::success('登录成功', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\logs;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\logs\LoginLogQueryRequest;
|
||||
use app\adminapi\schema\response\system\LoginLogResponse;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\logs\LoginLogService;
|
||||
use core\exception\handler\AdminException;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class LoginLogController extends Crud
|
||||
{
|
||||
public function __construct(LoginLogService $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/logs/login',
|
||||
summary: '列表',
|
||||
tags: ['日志管理.登录'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => LoginLogQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'logs:login:list')]
|
||||
#[PageResponse(schema: LoginLogResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
[$where, $format, $limit, $field, $order, $page] = $this->selectInput($request);
|
||||
$methods = [
|
||||
'select' => 'formatSelect',
|
||||
'tree' => 'formatTree',
|
||||
'table_tree' => 'formatTableTree',
|
||||
'normal' => 'formatNormal',
|
||||
];
|
||||
$format_function = $methods[$format] ?? 'formatNormal';
|
||||
$total = $this->service->getCount($where);
|
||||
$list = $this->service->selectList($where, $field, $page, $limit, $order, ['account' => function ($query) {
|
||||
$query->select(['id', 'user_name', 'real_name']);
|
||||
}], false);
|
||||
return call_user_func([$this, $format_function], $list, $total);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/logs/login/{id}',
|
||||
summary: '详情',
|
||||
tags: ['日志管理.登录'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: '日志ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: 1)
|
||||
)]
|
||||
#[Permission(code: 'logs:login:read')]
|
||||
#[SimpleResponse(schema: LoginLogResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $this->service->get($id,['*'],['account' => function ($query) {
|
||||
$query->select(['id', 'user_name', 'real_name']);
|
||||
}]);
|
||||
if (empty($data)) {
|
||||
throw new AdminException('数据未找到');
|
||||
}
|
||||
return Json::success('ok', $data->toArray());
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/logs/login/{id}',
|
||||
summary: '删除',
|
||||
tags: ['日志管理.登录'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'logs:login:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/logs/login',
|
||||
summary: '批量删除',
|
||||
tags: ['日志管理.登录'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'logs:login:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\logs;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\logs\OperateLogQueryRequest;
|
||||
use app\adminapi\schema\response\system\OperateLogResponse;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\logs\OperateLogService;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class OperateLogController extends Crud
|
||||
{
|
||||
public function __construct(OperateLogService $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/logs/operate',
|
||||
summary: '列表',
|
||||
tags: ['日志管理.操作'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => OperateLogQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'logs:operate:list')]
|
||||
#[PageResponse(schema: OperateLogResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/logs/operate/{id}',
|
||||
summary: '详情',
|
||||
tags: ['日志管理.操作'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'logs:operate:read')]
|
||||
#[SimpleResponse(schema: OperateLogResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/logs/operate/{id}',
|
||||
summary: '删除',
|
||||
tags: ['日志管理.操作'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: 'ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', default: '123456789012345678'),
|
||||
)]
|
||||
#[Permission(code: 'logs:operate:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/logs/operate',
|
||||
summary: '批量删除',
|
||||
tags: ['日志管理.操作'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'logs:operate:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\member;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\validate\member\MemberLevelValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\member\MemberLevelService;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class LevelController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(MemberLevelService $service, MemberLevelValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/level',
|
||||
summary: '列表',
|
||||
tags: ['会员等级'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "name", description: "等级名称", in: "query", schema: new OA\Schema(type: "string")),
|
||||
new OA\Parameter(name: "status", description: "状态", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "page", description: "页码", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "limit", description: "每页数量", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
]
|
||||
)]
|
||||
#[Permission("member:level:list")]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/member/level',
|
||||
summary: '创建',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "name", type: "string", example: "普通会员"),
|
||||
new OA\Property(property: "min_points", type: "integer", example: 0),
|
||||
new OA\Property(property: "max_points", type: "integer", example: 999),
|
||||
new OA\Property(property: "discount", type: "number", example: 1),
|
||||
new OA\Property(property: "status", type: "integer", example: 1),
|
||||
new OA\Property(property: "sort", type: "integer", example: 100),
|
||||
])
|
||||
),
|
||||
tags: ['会员等级'],
|
||||
)]
|
||||
#[Permission("member:level:create")]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
return parent::store($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/member/level/{id}',
|
||||
summary: '更新',
|
||||
tags: ['会员等级'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission("member:level:update")]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
return parent::update($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/member/level/{id}',
|
||||
summary: '删除会员等级',
|
||||
tags: ['会员等级'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission("member:level:delete")]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/member/level',
|
||||
summary: '批量删除',
|
||||
tags: ['会员等级'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class],
|
||||
)]
|
||||
#[Permission("member:level:delete")]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/level/{id}',
|
||||
summary: '详情',
|
||||
tags: ['会员等级'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission("member:level:show")]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\member;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\CurrentUser;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\validate\member\MemberAuthValidate;
|
||||
use app\service\admin\member\MemberAuthService;
|
||||
use app\service\admin\member\MemberService;
|
||||
use app\service\admin\member\MemberTagPermissionService;
|
||||
use app\service\admin\member\MemberTagRelationService;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class MemberAuthController extends Crud
|
||||
{
|
||||
public function __construct(MemberAuthService $service, MemberAuthValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/auth/permissions',
|
||||
summary: '权限菜单-包含所有',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关-会员'],
|
||||
)]
|
||||
#[Permission(code: 'member:auth:permissions')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function getPermissions(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$format = input('format', 'table_tree');
|
||||
$collection = $this->service->getAllMenus(true);
|
||||
$methods = [
|
||||
'select' => 'formatSelect',
|
||||
'tree' => 'formatTree',
|
||||
'table_tree' => 'formatTableTree',
|
||||
'normal' => 'formatNormal',
|
||||
];
|
||||
$format_function = $methods[$format] ?? 'formatNormal';
|
||||
$items = $collection->toArray();
|
||||
return call_user_func([$this, $format_function], $collection, count($items));
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/auth/{id}/tag-permissions',
|
||||
summary: '权限菜单-包含所有',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关-会员'],
|
||||
)]
|
||||
#[Permission(code: 'member:auth:tag_permissions')]
|
||||
#[SimpleResponse(example: '{"code": 0,"msg": "ok","data": []}')]
|
||||
public function getPermissionsByTag(Request $request, $id): \support\Response
|
||||
{
|
||||
try {
|
||||
$format = input('format', 'tree');
|
||||
$collection = $this->service->getMenusByMemberTags($id, true);
|
||||
$methods = [
|
||||
'select' => 'formatSelect',
|
||||
'tree' => 'formatTree',
|
||||
'table_tree' => 'formatTableTree',
|
||||
'normal' => 'formatNormal',
|
||||
];
|
||||
$format_function = $methods[$format] ?? 'formatNormal';
|
||||
$items = $collection->toArray();
|
||||
return call_user_func([$this, $format_function], $items, count($items));
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/auth/perm-code',
|
||||
summary: '获取用户-权限码',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关-会员'],
|
||||
)]
|
||||
#[Permission(code: 'member:auth:perm_code')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => ['admin']])]
|
||||
public function getUserCodes(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
/** @var CurrentUser $currUser */
|
||||
$currUser = Container::make(CurrentUser::class);
|
||||
$data = $this->service->getCodesByUserRoles($currUser);
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/auth/tag-menu-ids',
|
||||
summary: '通过角色ID获取权限ID集合',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关-会员'],
|
||||
)]
|
||||
#[Permission(code: 'member:auth:role_menu_ids')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function tagMenuIds(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$tagId = $request->input('tag_id');
|
||||
$result = $this->service->getMenusByTagId($tagId, true);
|
||||
if (empty($result)) {
|
||||
return Json::Success([]);
|
||||
}
|
||||
$data = [];
|
||||
foreach ($result as $item) {
|
||||
$data[] = $item->id;
|
||||
}
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[OA\Post(
|
||||
path: '/member/auth/save-tag-menu',
|
||||
summary: '保存角色菜单关系',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关-会员'],
|
||||
)]
|
||||
#[Permission(code: 'member:auth:role_menu')]
|
||||
#[SimpleResponse( example: '{"code": 0,"msg": "ok"}')]
|
||||
public function saveTagMenuRelation(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->all();
|
||||
/** @var MemberTagPermissionService $systemRoleMenuService */
|
||||
$systemRoleMenuService = Container::make(MemberTagPermissionService::class);
|
||||
$systemRoleMenuService->save($data);
|
||||
return Json::success('success');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/auth/user-list-by-tag-id',
|
||||
summary: '获取角色-关联用户列表',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关-会员'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'tag_id',
|
||||
description: '标签ID',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string'),
|
||||
)]
|
||||
#[Permission(code: 'member:auth:tag_user_list')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function getUsersByTagId(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
[$where, $format, $limit, $field, $order, $page] = $this->selectInput($request, [
|
||||
'allowed_fields' => ['username', 'nickname', 'email', 'mobile', 'tag_id'], // 使用 null 表示不限制字段,允许所有模型字段
|
||||
'skip_fields' => [],
|
||||
// 添加自定义参数会自动合并到 where 中
|
||||
'custom_fields' => [],
|
||||
]);
|
||||
/** @var MemberService $systemUserService */
|
||||
$systemUserService = Container::make(MemberService::class);
|
||||
$data = $systemUserService->getUsersListByTagId($where, $field, $page, $limit);
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/member/auth/save-user-tag',
|
||||
summary: '保存用户-关联标签',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关-会员'],
|
||||
)]
|
||||
#[Permission(code: 'member:auth:save_user_tag')]
|
||||
#[SimpleResponse(example: '{"code": 0,"msg": "success"}')]
|
||||
public function saveUserTags(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->all();
|
||||
/** @var MemberTagRelationService $memberTagRelationService */
|
||||
$memberTagRelationService = Container::make(MemberTagRelationService::class);
|
||||
$memberTagRelationService->saveUserTags($data);
|
||||
return Json::success('success');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/member/auth/remove-user-tag',
|
||||
summary: '移除用户-关联角色',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关-会员'],
|
||||
)]
|
||||
#[Permission(code: 'member:auth:remove_user_tag')]
|
||||
#[SimpleResponse(example: '{"code": 0,"msg": "ok"}')]
|
||||
public function removeUserRole(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->all();
|
||||
/** @var MemberTagRelationService $MemberTagRelationService */
|
||||
$MemberTagRelationService = Container::make(MemberTagRelationService::class);
|
||||
$MemberTagRelationService->removeUserTag($data);
|
||||
return Json::success('ok');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/auth/user-list-exclude-tag-id',
|
||||
summary: '获取用户列表-排除指定角色id',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关-会员'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'tag_id',
|
||||
description: '标签ID',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string'),
|
||||
)]
|
||||
#[Permission(code: 'member:auth:user_list_exclude_tag')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function getUsersExcludingTag(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
// 自定义参数
|
||||
$tagId = $request->input('tag_id');
|
||||
[$where, $format, $limit, $field, $order, $page] = $this->selectInput($request, [
|
||||
'allowed_fields' => null, // 使用 null 表示不限制字段,允许所有模型字段
|
||||
'skip_fields' => ['token', 'deleted_at'],
|
||||
// 添加自定义参数 tag_id,会自动合并到 where 中
|
||||
'custom_fields' => [
|
||||
'tag_id' => $tagId,
|
||||
],
|
||||
]);
|
||||
|
||||
/** @var MemberService $memberUserService */
|
||||
$memberUserService = Container::make(MemberService::class);
|
||||
$data = $memberUserService->getUsersExcludingTag($where, $field, $page, $limit);
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,409 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\member;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\validate\member\MemberValidate;
|
||||
use app\api\CurrentMember;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\member\MemberService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class MemberController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(MemberService $service, MemberValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/user',
|
||||
summary: '会员用户列表',
|
||||
tags: ['会员用户'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "username", description: "用户名", in: "query", schema: new OA\Schema(type: "string")),
|
||||
new OA\Parameter(name: "nickname", description: "昵称", in: "query", schema: new OA\Schema(type: "string")),
|
||||
new OA\Parameter(name: "phone", description: "手机号", in: "query", schema: new OA\Schema(type: "string")),
|
||||
new OA\Parameter(name: "email", description: "邮箱", in: "query", schema: new OA\Schema(type: "string")),
|
||||
new OA\Parameter(name: "level_id", description: "等级ID", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "status", description: "状态", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "page", description: "页码", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "limit", description: "每页数量", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "获取成功"),
|
||||
new OA\Property(property: "data", properties: [
|
||||
new OA\Property(property: "total", type: "integer", example: 10),
|
||||
new OA\Property(property: "list", type: "array", items: new OA\Items(properties: [
|
||||
new OA\Property(property: "id", type: "integer", example: 1),
|
||||
new OA\Property(property: "username", type: "string", example: "testuser"),
|
||||
new OA\Property(property: "nickname", type: "string", example: "测试用户"),
|
||||
new OA\Property(property: "phone", type: "string", example: "13800138000"),
|
||||
new OA\Property(property: "email", type: "string", example: "test@example.com"),
|
||||
new OA\Property(property: "avatar", type: "string", example: ""),
|
||||
new OA\Property(property: "level_id", type: "integer", example: 1),
|
||||
new OA\Property(property: "status", type: "integer", example: 1),
|
||||
new OA\Property(property: "create_time", type: "integer", example: 1640995200),
|
||||
])),
|
||||
], type: "object"),
|
||||
])
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission("member:user:list")]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
[$where, $format, $limit, $field, $order, $page] = $this->selectInput($request);
|
||||
$methods = [
|
||||
'select' => 'formatSelect',
|
||||
'tree' => 'formatTree',
|
||||
'table_tree' => 'formatTableTree',
|
||||
'normal' => 'formatNormal',
|
||||
];
|
||||
$format_function = $methods[$format] ?? 'formatNormal';
|
||||
$total = $this->service->getCount($where);
|
||||
$list = $this->service->selectList($where, $field, $page, $limit, $order, ['level', 'tags'], false);
|
||||
return call_user_func([$this, $format_function], $list, $total);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/user/{id}',
|
||||
summary: '会员用户详情',
|
||||
tags: ['会员用户'],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "获取成功"),
|
||||
new OA\Property(property: "data", properties: [
|
||||
new OA\Property(property: "id", type: "integer", example: 1),
|
||||
new OA\Property(property: "username", type: "string", example: "testuser"),
|
||||
new OA\Property(property: "nickname", type: "string", example: "测试用户"),
|
||||
new OA\Property(property: "phone", type: "string", example: "13800138000"),
|
||||
new OA\Property(property: "email", type: "string", example: "test@example.com"),
|
||||
new OA\Property(property: "avatar", type: "string", example: ""),
|
||||
new OA\Property(property: "level_id", type: "integer", example: 1),
|
||||
new OA\Property(property: "status", type: "integer", example: 1),
|
||||
new OA\Property(property: "create_time", type: "integer", example: 1640995200),
|
||||
], type: "object"),
|
||||
])
|
||||
),
|
||||
],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission("member:user:read")]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/member/user',
|
||||
summary: '创建',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "username", type: "string", example: "testuser"),
|
||||
new OA\Property(property: "nickname", type: "string", example: "测试用户"),
|
||||
new OA\Property(property: "phone", type: "string", example: "13800138000"),
|
||||
new OA\Property(property: "email", type: "string", example: "test@example.com"),
|
||||
new OA\Property(property: "password", type: "string", example: "123456"),
|
||||
new OA\Property(property: "avatar", type: "string", example: ""),
|
||||
new OA\Property(property: "level_id", type: "integer", example: 1),
|
||||
new OA\Property(property: "status", type: "integer", example: 1),
|
||||
])
|
||||
),
|
||||
tags: ['会员用户'],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "创建成功"),
|
||||
new OA\Property(property: "data", properties: [
|
||||
new OA\Property(property: "id", type: "integer", example: 1),
|
||||
], type: "object"),
|
||||
])
|
||||
),
|
||||
],
|
||||
)]
|
||||
#[Permission("member:user:create")]
|
||||
#[SimpleResponse( example: '{"code":0,"msg":"created","id":1}')]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
return parent::store($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/member/user/{id}',
|
||||
summary: '更新',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "nickname", type: "string", example: "新昵称"),
|
||||
new OA\Property(property: "phone", type: "string", example: "13800138000"),
|
||||
new OA\Property(property: "email", type: "string", example: "test@example.com"),
|
||||
new OA\Property(property: "avatar", type: "string", example: ""),
|
||||
new OA\Property(property: "level_id", type: "integer", example: 2),
|
||||
new OA\Property(property: "status", type: "integer", example: 1),
|
||||
])
|
||||
),
|
||||
tags: ['会员用户'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "用户ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "更新成功"),
|
||||
new OA\Property(property: "data", properties: [
|
||||
new OA\Property(property: "id", type: "integer", example: 1),
|
||||
], type: "object"),
|
||||
])
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission("member:user:update")]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
|
||||
return parent::update($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/member/user/{id}',
|
||||
summary: '删除',
|
||||
tags: ['会员用户'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "用户ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "删除成功"),
|
||||
])
|
||||
),
|
||||
],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission("member:user:delete")]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/member/user',
|
||||
summary: '批量删除',
|
||||
tags: ['会员用户'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class],
|
||||
)]
|
||||
#[Permission("member:user:delete")]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/member/user/{id}/reset-password',
|
||||
summary: '重置密码',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "password", type: "string", example: "123456"),
|
||||
])
|
||||
),
|
||||
tags: ['会员用户'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "用户ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "密码重置成功"),
|
||||
])
|
||||
),
|
||||
],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission("member:user:reset_password")]
|
||||
public function resetPassword(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $this->insertInput($request);
|
||||
if (empty($data['password'])) {
|
||||
return Json::fail('密码不能为空');
|
||||
}
|
||||
$this->service->update($id, ['password' => $data['password']]);
|
||||
return Json::success('密码重置成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/member/user/{id}/assign-tags',
|
||||
summary: '分配标签',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "tags", type: "array", example: [1, 2, 3]),
|
||||
])
|
||||
),
|
||||
tags: ['会员用户'],
|
||||
)]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
#[Permission("member:user:assign_tags")]
|
||||
public function assignTags(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $request->input('tags', []);
|
||||
if (empty($data)) {
|
||||
return Json::fail('标签不能为空');
|
||||
}
|
||||
$this->service->assignTags($id, $data);
|
||||
return Json::success('标签分配成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/member/user/{id}/adjust-points',
|
||||
summary: '调整会员积分',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "points", type: "integer", example: 100),
|
||||
])
|
||||
),
|
||||
tags: ['会员用户'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "用户ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
#[Permission("member:user:adjust_points")]
|
||||
public function adjustPoints(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$points = $request->input('points', 0);
|
||||
$this->service->adjustPoints($id, (int)$points);
|
||||
return Json::success('积分调整成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化下拉列表
|
||||
*
|
||||
* @param $items
|
||||
*
|
||||
* @return \support\Response
|
||||
*/
|
||||
public function formatSelect($items): \support\Response
|
||||
{
|
||||
$formatted_items = [];
|
||||
foreach ($items as $item) {
|
||||
$formatted_items[] = [
|
||||
'label' => $item->nickname ?? $item->id,
|
||||
'value' => $item->id,
|
||||
];
|
||||
}
|
||||
return Json::success('ok', $formatted_items);
|
||||
}
|
||||
|
||||
// #[OA\Get(
|
||||
// path: '/member/user/statistics',
|
||||
// summary: '会员统计',
|
||||
// tags: ['会员用户'],
|
||||
// responses: [
|
||||
// new OA\Response(
|
||||
// response: 200,
|
||||
// description: "成功",
|
||||
// content: new OA\JsonContent(properties: [
|
||||
// new OA\Property(property: "code", type: "integer", example: 0),
|
||||
// new OA\Property(property: "msg", type: "string", example: "获取成功"),
|
||||
// new OA\Property(property: "data", properties: [
|
||||
// new OA\Property(property: "total", type: "integer", example: 100),
|
||||
// new OA\Property(property: "active", type: "integer", example: 80),
|
||||
// new OA\Property(property: "inactive", type: "integer", example: 20),
|
||||
// ], type: "object"),
|
||||
// ])
|
||||
// ),
|
||||
// ]
|
||||
// )]
|
||||
// #[Permission("member:user:statistics")]
|
||||
public function statistics(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$result = $this->service->getStatistics();
|
||||
return Json::success('获取成功', $result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\member;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\validate\member\MemberPointsValidate;
|
||||
use app\service\admin\member\MemberPointsService;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use core\exception\handler\AdminException;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class PointsController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(MemberPointsService $service, MemberPointsValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/points',
|
||||
summary: '列表',
|
||||
tags: ['会员积分'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "member_id", description: "会员ID", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "type", description: "类型", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "start_time", description: "开始时间", in: "query", schema: new OA\Schema(type: "string")),
|
||||
new OA\Parameter(name: "end_time", description: "结束时间", in: "query", schema: new OA\Schema(type: "string")),
|
||||
new OA\Parameter(name: "page", description: "页码", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "limit", description: "每页数量", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
]
|
||||
)]
|
||||
#[Permission("member:points:list")]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
[$where, $format, $limit, $field, $order, $page] = $this->selectInput($request);
|
||||
$methods = [
|
||||
'select' => 'formatSelect',
|
||||
'tree' => 'formatTree',
|
||||
'table_tree' => 'formatTableTree',
|
||||
'normal' => 'formatNormal',
|
||||
];
|
||||
$format_function = $methods[$format] ?? 'formatNormal';
|
||||
$total = $this->service->getCount($where);
|
||||
$list = $this->service->selectList($where, $field, $page, $limit, $order, ['member'], false);
|
||||
return call_user_func([$this, $format_function], $list, $total);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/points/{id}',
|
||||
summary: '详情',
|
||||
tags: ['会员积分'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission("member:points:read")]
|
||||
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $this->service->get($id, ['*'], ['member'], 'created_at', []);
|
||||
if (empty($data)) {
|
||||
throw new AdminException('数据未找到', ['errorCode' => 400]);
|
||||
}
|
||||
return Json::success('ok', $data->toArray());
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/member/points',
|
||||
summary: '新增',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "member_id", type: "integer", example: 1),
|
||||
new OA\Property(property: "points", type: "integer", example: 100),
|
||||
new OA\Property(property: "type", type: "integer", example: 1),
|
||||
new OA\Property(property: "source", type: "string", example: "admin"),
|
||||
new OA\Property(property: "remark", type: "string", example: "管理员操作"),
|
||||
new OA\Property(property: "operator", type: "string", example: "admin"),
|
||||
])
|
||||
),
|
||||
tags: ['会员积分']
|
||||
)]
|
||||
#[Permission("member:points:create")]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->all();
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
if (!$this->validate->scene('store')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
$this->service->operate($data);
|
||||
return Json::success('操作成功');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\member;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\validate\member\MemberTagValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\member\MemberTagService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class TagController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(MemberTagService $service, MemberTagValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/tag',
|
||||
summary: '列表',
|
||||
tags: ['会员标签'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "name", description: "标签名称", in: "query", schema: new OA\Schema(type: "string")),
|
||||
new OA\Parameter(name: "status", description: "状态", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "page", description: "页码", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "limit", description: "每页数量", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
]
|
||||
)]
|
||||
#[Permission("member:tag:list")]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/tag/{id}',
|
||||
summary: '详情',
|
||||
tags: ['会员标签'],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "获取成功"),
|
||||
new OA\Property(property: "data", properties: [
|
||||
new OA\Property(property: "id", type: "integer", example: 1),
|
||||
new OA\Property(property: "name", type: "string", example: "活跃会员"),
|
||||
new OA\Property(property: "color", type: "string", example: "#409EFF"),
|
||||
new OA\Property(property: "status", type: "integer", example: 1),
|
||||
new OA\Property(property: "sort", type: "integer", example: 100),
|
||||
new OA\Property(property: "create_time", type: "integer", example: 1640995200),
|
||||
], type: "object"),
|
||||
])
|
||||
),
|
||||
],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission("member:tag:read")]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/member/tag',
|
||||
summary: '新增',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "name", type: "string", example: "活跃会员"),
|
||||
new OA\Property(property: "color", type: "string", example: "#409EFF"),
|
||||
new OA\Property(property: "status", type: "integer", example: 1),
|
||||
new OA\Property(property: "sort", type: "integer", example: 100),
|
||||
])
|
||||
),
|
||||
tags: ['会员标签']
|
||||
)]
|
||||
#[Permission("member:tag:create")]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
return parent::store($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/member/tag/{id}',
|
||||
summary: '更新',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "name", type: "string", example: "活跃会员"),
|
||||
new OA\Property(property: "color", type: "string", example: "#409EFF"),
|
||||
new OA\Property(property: "status", type: "integer", example: 1),
|
||||
new OA\Property(property: "sort", type: "integer", example: 100),
|
||||
])
|
||||
),
|
||||
tags: ['会员标签'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "标签ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "更新成功"),
|
||||
new OA\Property(property: "data", properties: [
|
||||
new OA\Property(property: "id", type: "integer", example: 1),
|
||||
], type: "object"),
|
||||
])
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission("member:tag:update")]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
return parent::update($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/member/tag/{id}',
|
||||
summary: '删除',
|
||||
tags: ['会员标签'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "标签ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission("member:tag:delete")]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/member/tag',
|
||||
summary: '批量删除',
|
||||
tags: ['会员标签'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class],
|
||||
)]
|
||||
#[Permission("member:tag:delete")]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/member/{id}/permissions',
|
||||
summary: '为标签分配权限',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "permissions", type: "array", items: new OA\Items(type: "string"), example: ["developer", "api_access"]),
|
||||
])
|
||||
),
|
||||
tags: ['会员标签'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "标签ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
)]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
#[Permission("member:tag:assign_permissions")]
|
||||
public function assignPermissions(Request $request, $id): \support\Response
|
||||
{
|
||||
try {
|
||||
$permissions = $request->input('permissions', []);
|
||||
$this->service->assignPermissions($id, $permissions);
|
||||
return Json::success('权限分配成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/{id}/permissions',
|
||||
summary: '获取标签权限',
|
||||
tags: ['会员标签'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "标签ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
)]
|
||||
#[Permission("member:tag:show_permissions")]
|
||||
public function getPermissions(Request $request, $id): \support\Response
|
||||
{
|
||||
try {
|
||||
$tag = $this->service->get($id, ['*'], ['permissions']);
|
||||
if (!$tag) {
|
||||
return Json::fail('标签不存在');
|
||||
}
|
||||
return Json::success('ok', $tag->permissions->toArray());
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/all-with-permissions',
|
||||
summary: '获取所有启用的标签(含权限)',
|
||||
tags: ['会员标签'],
|
||||
)]
|
||||
#[Permission("member:tag:all")]
|
||||
public function getAllWithPermissions(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$result = $this->service->getEnabledTagsWithPermissions();
|
||||
return Json::success('ok', $result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/member/available-permissions',
|
||||
summary: '获取所有可用的权限码(从菜单表)',
|
||||
tags: ['会员标签'],
|
||||
)]
|
||||
#[Permission("member:tag:all")]
|
||||
public function getAvailablePermissions(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$result = $this->service->getAvailablePermissions();
|
||||
return Json::success('ok', $result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\message;
|
||||
|
||||
use app\adminapi\CurrentUser;
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\message\MessageFormRequest;
|
||||
use app\adminapi\schema\request\message\MessageQueryRequest;
|
||||
use app\adminapi\schema\response\system\MessageResponse;
|
||||
use app\adminapi\validate\system\MessageValidate;
|
||||
use app\enum\system\MessageStatus;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\message\MessageService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\AllowAnonymous;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use OpenApi\Attributes\RequestBody;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use support\Container;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class MessageController extends Crud
|
||||
{
|
||||
public function __construct(MessageService $service, MessageValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/message',
|
||||
summary: '列表',
|
||||
tags: ['消息管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => MessageQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'network:message:list')]
|
||||
#[PageResponse(MessageResponse::class)]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
$uid = Container::make(CurrentUser::class)->id();
|
||||
$data = $request->get();
|
||||
$data['receiver_id'] = $uid;//消息接收人
|
||||
$request->setGet($data);
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/message/{id}',
|
||||
summary: '详情',
|
||||
tags: ['消息管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'network:message:read')]
|
||||
#[SimpleResponse(schema: MessageResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/message',
|
||||
summary: '创建',
|
||||
tags: ['消息管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => MessageFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'network:message:create')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
return parent::store($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/message/{id}',
|
||||
summary: '更新',
|
||||
tags: ['消息管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => MessageFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: '消息ID(雪花ID)',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(
|
||||
type: 'string',
|
||||
example: '123456789012345678'
|
||||
)
|
||||
)]
|
||||
#[Permission(code: 'network:message:update')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
return parent::update($request);
|
||||
}
|
||||
#[OA\Post(
|
||||
path: '/message/export',
|
||||
summary: '导出',
|
||||
tags: ['消息管理']
|
||||
)]
|
||||
#[Permission(code: 'network:message:list', description: '导出')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function export(Request $request): \support\Response
|
||||
{
|
||||
return parent::export($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/message/{id}',
|
||||
summary: '删除',
|
||||
tags: ['消息管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'id',
|
||||
description: '消息ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(
|
||||
type: 'string',
|
||||
default: 0,
|
||||
example: '123456789012345678',
|
||||
)
|
||||
),
|
||||
],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'network:message:delete')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/message',
|
||||
summary: '批量删除',
|
||||
tags: ['消息管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'network:message:delete')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/message/{id}/update-read',
|
||||
summary: '消息状态标记',
|
||||
tags: ['消息管理'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: '消息ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(
|
||||
type: 'string',
|
||||
default: 0,
|
||||
example: '123456789012345678',
|
||||
)
|
||||
)]
|
||||
#[RequestBody(required: true, content: new OA\JsonContent(
|
||||
required: ['status'],
|
||||
properties: [
|
||||
new OA\Property(
|
||||
property: 'status',
|
||||
description: '消息状态(0=未读,1=已读)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1
|
||||
),
|
||||
]
|
||||
))]
|
||||
#[Permission(code: 'network:message:update_read')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function updateRead(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->all();
|
||||
if ($this->validate) {
|
||||
$this->validate->scene('update-read')->check($data);
|
||||
}
|
||||
$actionMap = [
|
||||
MessageStatus::READ->value => 'isRead',
|
||||
MessageStatus::UNREAD->value => 'isUnread',
|
||||
];
|
||||
|
||||
if (!isset($actionMap[$data['status']])) {
|
||||
throw new \InvalidArgumentException('Invalid message status');
|
||||
}
|
||||
|
||||
$this->service->{$actionMap[$data['status']]}(
|
||||
$data['id'] ?? throw new \InvalidArgumentException('Missing message ID')
|
||||
);
|
||||
return Json::success('ok');
|
||||
} catch (\Throwable $e) {
|
||||
// 统一异常处理(生产环境建议记录日志)
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/message/notify-on-first-login-to-all',
|
||||
summary: '推送广播&消息',
|
||||
tags: ['消息管理'],
|
||||
)]
|
||||
#[Permission(code: 'network:message:push')]
|
||||
#[AllowAnonymous(requireToken: true,requirePermission: false)]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function notifyOnFirstLoginToAll(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = Container::make(CurrentUser::class)->id();
|
||||
$this->service->notifyOnFirstLoginToAll($id);
|
||||
return Json::success('ok');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\controller\monitor;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\service\admin\monitor\OnlineUserService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class OnlineUserController extends Crud
|
||||
{
|
||||
#[OA\Get(
|
||||
path: '/monitor/online-user',
|
||||
summary: '在线用户列表',
|
||||
tags: ['系统监控']
|
||||
)]
|
||||
#[Permission(code: 'monitor:online_user:list')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
/** @var OnlineUserService $service */
|
||||
$service = Container::get(OnlineUserService::class);
|
||||
return Json::success('ok', $service->list($request->all()));
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/monitor/online-user/{jti}',
|
||||
summary: '踢下线',
|
||||
tags: ['系统监控']
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'jti',
|
||||
description: '会话JTI',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[Permission(code: 'monitor:online_user:kickout')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$jti = (string)$request->route->param('jti', '');
|
||||
if ($jti === '') {
|
||||
return Json::fail('会话JTI不能为空');
|
||||
}
|
||||
|
||||
/** @var OnlineUserService $service */
|
||||
$service = Container::get(OnlineUserService::class);
|
||||
if (!$service->kickout($jti)) {
|
||||
return Json::fail('会话不存在或已失效');
|
||||
}
|
||||
|
||||
return Json::success('ok');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/monitor/online-user/{jti}/revoke',
|
||||
summary: '撤销Token',
|
||||
tags: ['系统监控']
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'jti',
|
||||
description: '会话JTI',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[Permission(code: 'monitor:online_user:revoke')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function revoke(Request $request): \support\Response
|
||||
{
|
||||
return $this->destroy($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\monitor;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\response\monitor\RedisMonitorResponse;
|
||||
use core\monitor\ServerMonitor;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class RedisController extends Crud
|
||||
{
|
||||
|
||||
#[OA\Get(
|
||||
path: '/monitor/redis',
|
||||
summary: 'Redis监控',
|
||||
tags: ['系统监控']
|
||||
)]
|
||||
#[Permission(code: 'monitor:redis:read')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => RedisMonitorResponse::class])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
/** @var ServerMonitor $service */
|
||||
$service = Container::get(ServerMonitor::class);
|
||||
$data = $service->getRedisInfo();
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\monitor;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\response\monitor\ServerMonitorResponse;
|
||||
use core\monitor\ServerMonitor;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class ServerController extends Crud
|
||||
{
|
||||
|
||||
#[OA\Get(
|
||||
path: '/monitor/server',
|
||||
summary: '性能监控',
|
||||
tags: ['系统监控']
|
||||
)]
|
||||
#[Permission(code: 'monitor:server:read')]
|
||||
#[SimpleResponse(schema:ServerMonitorResponse::class, example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
/** @var ServerMonitor $service */
|
||||
$service = Container::get(ServerMonitor::class);
|
||||
$serverInfo = [
|
||||
'cpu' => $service->getCpuInfo(),
|
||||
'memory' => $service->getMemoryInfo(),
|
||||
'disk' => $service->getDiskInfo(),
|
||||
'php' => $service->getPhpInfo(),
|
||||
];
|
||||
|
||||
return Json::success('ok', $serverInfo);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\notice;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\notice\NoticeFormRequest;
|
||||
use app\adminapi\schema\request\notice\NoticeQueryRequest;
|
||||
use app\adminapi\schema\response\system\NoticeResponse;
|
||||
use app\adminapi\validate\system\NoticeValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\notice\NoticeService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use Webman\RedisQueue\Client;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class NoticeController extends Crud
|
||||
{
|
||||
// 注入服务和验证器
|
||||
public function __construct(NoticeService $service, NoticeValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/notice',
|
||||
summary: '列表',
|
||||
tags: ['公告管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => NoticeQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'network:notice:list')]
|
||||
#[PageResponse(schema:NoticeResponse::class,example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request); // 调用父类Crud的列表方法
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/notice/{id}',
|
||||
summary: '详情',
|
||||
tags: ['公告管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'network:notice:read')]
|
||||
#[SimpleResponse(schema: NoticeResponse::class, example: ['code' => 0, 'message' => 'success', 'data' => []])] // 详情响应
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request); // 调用父类Crud的详情方法
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/notice',
|
||||
summary: '创建',
|
||||
tags: ['公告管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => NoticeFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'network:notice:create')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
return parent::store($request);
|
||||
}
|
||||
|
||||
// 4. 更新公告
|
||||
#[OA\Put(
|
||||
path: '/notice/{id}',
|
||||
summary: '更新',
|
||||
tags: ['公告管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => NoticeFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: 'ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: '123456789012345678')
|
||||
)]
|
||||
#[Permission(code: 'network:notice:update')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])] // 更新响应
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
return parent::update($request); // 调用父类Crud的更新方法
|
||||
}
|
||||
#[OA\Post(
|
||||
path: '/notice/export',
|
||||
summary: '导出',
|
||||
tags: ['公告管理']
|
||||
)]
|
||||
#[Permission(code: 'network:notice:list', description: '导出')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function export(Request $request): \support\Response
|
||||
{
|
||||
return parent::export($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/notice/{id}',
|
||||
summary: '删除',
|
||||
tags: ['公告管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'network:notice:delete')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])] // 删除响应
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request); // 调用父类Crud的删除方法
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/notice',
|
||||
summary: '批量删除',
|
||||
tags: ['公告管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'network:notice:delete')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/notice/publish',
|
||||
summary: '发布',
|
||||
tags: ['公告管理'],
|
||||
)]
|
||||
#[Permission(code: 'network:notice:publish')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function publish(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->input('id', null);
|
||||
$model = $this->service->get($id);
|
||||
$queue = 'admin-announcement-push';
|
||||
Client::send($queue, $model->makeVisible('tenant_id')->toArray(), 0);
|
||||
return Json::success('ok', []);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\org;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\org\DeptFormRequest;
|
||||
use app\adminapi\schema\request\org\DeptQueryRequest;
|
||||
use app\adminapi\schema\response\system\DeptResponse;
|
||||
use app\adminapi\validate\system\DeptValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\org\DeptService;
|
||||
use core\exception\handler\AdminException;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use OpenApi\Attributes\RequestBody;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class DeptController extends Crud
|
||||
{
|
||||
public function __construct(DeptService $service, DeptValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/org/dept',
|
||||
summary: '列表',
|
||||
tags: ['部门管理'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => DeptQueryRequest::class]
|
||||
)]
|
||||
#[Permission(code: 'org:dept:list')]
|
||||
#[PageResponse(DeptResponse::class)]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/org/dept',
|
||||
summary: '创建',
|
||||
tags: ['部门管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => DeptFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'org:dept:create')]
|
||||
#[SimpleResponse(DeptResponse::class, example: '{"code":0,"msg":"created","id":1}')]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->inputFilter($request->all(), ['leader_id_list']);
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
if (!$this->validate->scene('store')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
$model = $this->service->save($data);
|
||||
if (empty($model)) {
|
||||
throw new AdminException('插入失败');
|
||||
}
|
||||
$pk = $model->getPk();
|
||||
return Json::success('ok', [$pk => $model->getData($pk)]);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/org/dept/{id}',
|
||||
summary: '删除',
|
||||
tags: ['部门管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'org:dept:delete')]
|
||||
#[SimpleResponse( example: '{"code":0,"msg":"success","data":null}')]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/org/dept',
|
||||
summary: '批量删除',
|
||||
tags: ['部门管理'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class]
|
||||
)]
|
||||
#[Permission(code: 'org:dept:delete')]
|
||||
#[SimpleResponse(example: '{"code":0,"msg":"success","data":null}')]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/org/dept/{id}',
|
||||
summary: '更新',
|
||||
tags: ['部门管理'],
|
||||
)]
|
||||
#[Permission(code: 'org:dept:update')]
|
||||
#[SimpleResponse( example: '{"code":0,"msg":"success","data":null}')]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $this->inputFilter($request->all(), ['leader_id_list']);
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
if (!$this->validate->scene('update')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
$this->service->update($id, $data);
|
||||
return Json::success('ok');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/org/dept/{id}',
|
||||
summary: '详情',
|
||||
tags: ['部门管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'org:dept:read')]
|
||||
#[SimpleResponse(DeptResponse::class, example: '{"code":0,"msg":"success","data":null}')]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\org;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\org\PostFormRequest;
|
||||
use app\adminapi\schema\request\org\PostQueryRequest;
|
||||
use app\adminapi\schema\response\system\PostResponse;
|
||||
use app\adminapi\validate\system\PostValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\org\PostService;
|
||||
use core\exception\handler\AdminException;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class PostController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(PostService $service, PostValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/org/post',
|
||||
summary: '列表',
|
||||
tags: ['岗位管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => PostQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'org:post:list')]
|
||||
#[PageResponse(PostResponse::class, example: '{"code":0,"msg":"success","data":[]}')] // 分页响应关联岗位Schema
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
[$where, $format, $limit, $field, $order, $page] = $this->selectInput($request);
|
||||
$methods = [
|
||||
'select' => 'formatSelect',
|
||||
'tree' => 'formatTree',
|
||||
'table_tree' => 'formatTableTree',
|
||||
'normal' => 'formatNormal',
|
||||
];
|
||||
$format_function = $methods[$format] ?? 'formatNormal';
|
||||
$total = $this->service->getCount($where);
|
||||
$list = $this->service->selectList($where, $field, $page, $limit, $order, ['dept']);
|
||||
return call_user_func([$this, $format_function], $list, $total);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/org/post/{id}',
|
||||
summary: '详情',
|
||||
tags: ['岗位管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'org:post:read')]
|
||||
#[SimpleResponse(PostResponse::class, example: [])] // 详情响应
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $this->service->get($id,['*'],['dept']);
|
||||
if (empty($data)) {
|
||||
throw new AdminException('数据未找到');
|
||||
}
|
||||
return Json::success('ok', $data->toArray());
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/org/post',
|
||||
summary: '创建',
|
||||
tags: ['岗位管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => PostFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'org:post:create')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
return parent::store($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/org/post/{id}',
|
||||
summary: '更新',
|
||||
tags: ['岗位管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => PostFormRequest::class,
|
||||
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'org:post:update')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
return parent::update($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/org/post/{id}',
|
||||
summary: '删除',
|
||||
tags: ['岗位管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: 'ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: '123456789012345678')
|
||||
)]
|
||||
#[Permission(code: 'org:post:delete')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/org/post',
|
||||
summary: '批量删除',
|
||||
tags: ['岗位管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: 'ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: '123456789012345678')
|
||||
)]
|
||||
#[Permission(code: 'org:post:delete')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\controller\plugin;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\plugin\ApplicationAuthFormRequest;
|
||||
use app\adminapi\schema\request\plugin\ApplicationAuthQueryRequest;
|
||||
use app\adminapi\schema\response\plugin\ApplicationAuthResponse;
|
||||
use app\adminapi\validate\plugin\ApplicationAuthValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\plugin\ApplicationAuthService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class ApplicationAuthController extends Crud
|
||||
{
|
||||
public function __construct(ApplicationAuthService $service, ApplicationAuthValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/application-auth',
|
||||
summary: '列表',
|
||||
tags: ['应用授权'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => ApplicationAuthQueryRequest::class]
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:list')]
|
||||
#[PageResponse(schema: ApplicationAuthResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/application-auth/{id}',
|
||||
summary: '详情',
|
||||
tags: ['应用授权'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:read')]
|
||||
#[SimpleResponse(schema: ApplicationAuthResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/plugin/application-auth',
|
||||
summary: '创建',
|
||||
tags: ['应用授权'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => ApplicationAuthFormRequest::class]
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:create')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
return parent::store($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/plugin/application-auth/{id}',
|
||||
summary: '更新',
|
||||
tags: ['应用授权'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => ApplicationAuthFormRequest::class]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: 'ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: '123456789012345678')
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:update')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
return parent::update($request);
|
||||
}
|
||||
#[OA\Post(
|
||||
path: '/plugin/application-auth/export',
|
||||
summary: '导出',
|
||||
tags: ['应用授权']
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:list', description: '导出')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function export(Request $request): \support\Response
|
||||
{
|
||||
return parent::export($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/plugin/application-auth/{id}',
|
||||
summary: '删除',
|
||||
tags: ['应用授权'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:delete')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/plugin/application-auth',
|
||||
summary: '批量删除',
|
||||
tags: ['应用授权'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class]
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:delete')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/plugin/application-auth/{id}/reset-secret',
|
||||
summary: '重置密钥',
|
||||
tags: ['应用授权'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:reset-secret')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => ['id' => '123456789012345678', 'secret_key' => 'SK...']])]
|
||||
public function resetSecret(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
return Json::success('ok', $this->service->resetSecret($id));
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,482 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\plugin;
|
||||
|
||||
use app\adminapi\controller\Base;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\service\core\plugin\PluginDownloadService;
|
||||
use app\service\core\plugin\PluginInstallService;
|
||||
use app\service\core\plugin\PluginRemoteService;
|
||||
use app\service\core\plugin\PluginUninstallService;
|
||||
use app\service\core\plugin\PluginService;
|
||||
use core\tool\Sse;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class PluginController extends Base
|
||||
{
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin',
|
||||
summary: '列表',
|
||||
tags: ['模块市场'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'page',
|
||||
description: '页码',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', default: 1)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'limit',
|
||||
description: '每页数量',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', default: 10)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'type',
|
||||
description: '模块类型(module: 系统模块, plugin: 插件)',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string', enum: ['module', 'plugin'])
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'status',
|
||||
description: '状态(0:禁用,1:启用)',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', enum: [0, 1])
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'keyword',
|
||||
description: '搜索关键词',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'installed',
|
||||
description: '是否已安装(0:未安装,1:已安装)',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', enum: [0, 1])
|
||||
),
|
||||
],
|
||||
)]
|
||||
#[PageResponse(schema: [], example: [[
|
||||
'id' => 3,
|
||||
'code' => 'official',
|
||||
'name' => 'official',
|
||||
'type' => 'madong',
|
||||
'version' => '1.0.0',
|
||||
'status' => 1,
|
||||
'description' => '官方应用插件',
|
||||
'detail_description' => '',
|
||||
'author' => 'Madong',
|
||||
'cover' => '',
|
||||
'poster' => '',
|
||||
'price' => 0,
|
||||
'downloads' => 0,
|
||||
'rating' => 0,
|
||||
'created_at' => '2026-03-19 16:26:19',
|
||||
'updated_at' => '2026-03-19 16:26:19',
|
||||
'update_time' => '',
|
||||
'update_logs' => [],
|
||||
'category' => null,
|
||||
'category_name' => '',
|
||||
'tags' => [],
|
||||
'is_new' => 0,
|
||||
'is_hot' => 0,
|
||||
'purchased' => 0,
|
||||
'installed' => 0,
|
||||
'manual_uninstall' => 0,
|
||||
'composer_dependencies' => [],
|
||||
'npm_dependencies' => [],
|
||||
'is_installed' => 1,
|
||||
'installed_version' => '1.0.0',
|
||||
'has_update' => 0,
|
||||
'is_purchased' => 1,
|
||||
'is_downloaded' => 1,
|
||||
'can_uninstall' => 1,
|
||||
'can_download' => 1,
|
||||
'undeletable' => 1,
|
||||
'version_info' => [
|
||||
'remote' => [
|
||||
'version' => '1.0.0',
|
||||
'release_date' => '2026-03-19 16:26:19',
|
||||
],
|
||||
'local' => [
|
||||
'version' => '1.0.0',
|
||||
'install_date' => '2026-03-19 16:26:19',
|
||||
],
|
||||
'comparison' => [
|
||||
'needs_update' => 0,
|
||||
'is_latest' => 1,
|
||||
'version_difference' => null,
|
||||
],
|
||||
],
|
||||
]])]
|
||||
#[Permission(code: 'plugin:module:list')]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$category = $request->input('category', null);
|
||||
$pageInput = $request->input('current', $request->input('page', 1));
|
||||
$limitInput = $request->input('size', $request->input('limit', 15));
|
||||
$page = max(1, (int)$pageInput);
|
||||
$limit = max(1, (int)$limitInput);
|
||||
$type = $request->input('type');
|
||||
$keyword = $request->input('keyword');
|
||||
/** @var PluginService $service */
|
||||
$service = Container::make(PluginService::class);
|
||||
$result = $service->getList($category, $type, $keyword, $page, $limit);
|
||||
return Json::success('common.operation.success', $result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), []);
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/{name}/download',
|
||||
summary: '下载模块',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'name',
|
||||
description: '模块标识',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function download(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$pluginCode = $request->route->param('name');
|
||||
|
||||
// 1. 获取下载链接
|
||||
$remoteService = Container::make(PluginRemoteService::class);
|
||||
$authCode = config('madong.auth_code');
|
||||
$secretKey = config('madong.auth_secret');
|
||||
|
||||
$downloadInfo = $remoteService->getRemoteDownloadService($authCode, $secretKey, $pluginCode, 'latest');
|
||||
if (empty($downloadInfo['url'])) {
|
||||
return Json::fail('获取下载链接失败');
|
||||
}
|
||||
|
||||
// 2. 下载并解压插件
|
||||
$downloadService = Container::make(PluginDownloadService::class);
|
||||
$result = $downloadService->downloadAndExtract($pluginCode, $downloadInfo['url']);
|
||||
|
||||
return Json::success('下载成功', $result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/detail/{name}',
|
||||
summary: '获取模块详情',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'name',
|
||||
description: '模块标识',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function detail(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$moduleName = $request->route->param('name');
|
||||
/** @var PluginService $service */
|
||||
$service = Container::make(PluginService::class);
|
||||
$result = $service->getList(null, null, $moduleName);
|
||||
return Json::success('common.operation.success', $result['items'][0]);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), []);
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/{name}/upgrade-logs',
|
||||
summary: '获取模块升级日志',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'name',
|
||||
description: '模块标识',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function upgradeLogs(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$moduleName = $request->route->param('name');
|
||||
/** @var PluginService $service */
|
||||
$service = Container::make(PluginService::class);
|
||||
$result = $service->getUpgradeLogs($moduleName);
|
||||
|
||||
return Json::success('common.operation.success', $result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), []);
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/check-environment',
|
||||
summary: '环境检测',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'name',
|
||||
description: '模块标识',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function checkEnvironment(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$name = $request->input('name');
|
||||
/** @var PluginInstallService $service */
|
||||
$service = Container::make(PluginInstallService::class);
|
||||
$result = $service->installCheck($name);
|
||||
|
||||
// 提取路径列表和状态
|
||||
$paths = [];
|
||||
foreach ($result['checks'] as $check) {
|
||||
if (isset($check['path'])) {
|
||||
// 只展示从madong开始的路径
|
||||
$path = $check['path'];
|
||||
$madongPos = strpos(strtolower($path), 'madong');
|
||||
if ($madongPos !== false) {
|
||||
// 截取从madong开始的路径
|
||||
$path = substr($path, $madongPos);
|
||||
// 确保路径格式正确,madong后面应该有目录分隔符
|
||||
if (substr($path, 6, 1) !== DIRECTORY_SEPARATOR) {
|
||||
$path = 'madong' . DIRECTORY_SEPARATOR . substr($path, 6);
|
||||
}
|
||||
}
|
||||
$paths[] = [
|
||||
'path' => $path,
|
||||
'requirement' => $check['permission_type'] ?? 'readable',
|
||||
'status' => $check['status'] === 'success' ? 'success' : 'error',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return Json::success('common.operation.success', [
|
||||
'paths' => $paths,
|
||||
'overall_status' => $result['overall_status'],
|
||||
'message' => $result['message'],
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), []);
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/install',
|
||||
summary: '插件安装',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function install(Request $request): void
|
||||
{
|
||||
$connection = $request->connection;
|
||||
$code = $request->input('code');
|
||||
$mode = $request->input('mode', 'local');
|
||||
|
||||
// 验证参数
|
||||
if (empty($code)) {
|
||||
$connection->send(new Response(200, [
|
||||
'Content-Type' => 'text/event-stream',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Connection' => 'keep-alive',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Expose-Headers' => 'Content-Type',
|
||||
], "\r\n"));
|
||||
$connection->send(\core\tool\Sse::error('参数错误:缺少code参数'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送SSE响应头
|
||||
$connection->send(new Response(200, [
|
||||
'Content-Type' => 'text/event-stream',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Connection' => 'keep-alive',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Expose-Headers' => 'Content-Type',
|
||||
], "\r\n"));
|
||||
|
||||
try {
|
||||
// 发送开始安装消息
|
||||
$connection->send(Sse::progress('开始安装插件 ' . $code, 0));
|
||||
|
||||
// 调用安装服务
|
||||
$generator = (new PluginInstallService())->install($code, $mode);
|
||||
foreach ($generator as $chunk) {
|
||||
$connection->send($chunk);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 发送错误消息
|
||||
$connection->send(Sse::error('安装失败:' . $e->getMessage()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/uninstall',
|
||||
summary: '插件卸载(SSE流式)',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'code',
|
||||
description: '插件编码',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function uninstall(Request $request): void
|
||||
{
|
||||
$connection = $request->connection;
|
||||
$code = $request->input('code');
|
||||
// 验证参数
|
||||
if (empty($code)) {
|
||||
$connection->send(new Response(200, [
|
||||
'Content-Type' => 'text/event-stream',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Connection' => 'keep-alive',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Expose-Headers' => 'Content-Type',
|
||||
], "\r\n"));
|
||||
$connection->send(Sse::error('参数错误:缺少code参数'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送SSE响应头
|
||||
$connection->send(new Response(200, [
|
||||
'Content-Type' => 'text/event-stream',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Connection' => 'keep-alive',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Expose-Headers' => 'Content-Type',
|
||||
], "\r\n"));
|
||||
|
||||
try {
|
||||
// 调用卸载服务(统一入口)
|
||||
$generator = (new PluginUninstallService())->uninstall($code);
|
||||
foreach ($generator as $chunk) {
|
||||
$connection->send($chunk);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 发送错误消息
|
||||
$connection->send(Sse::error('卸载失败:' . $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/plugin/{name}',
|
||||
summary: '插件删除',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'name',
|
||||
description: '插件名称',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[Permission(code: 'plugin:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$name = $request->route->param('name');
|
||||
if (empty($name)) {
|
||||
return Json::fail('参数错误:缺少name参数', []);
|
||||
}
|
||||
|
||||
/** @var PluginUninstallService $service */
|
||||
$service = Container::get(PluginUninstallService::class);
|
||||
$service->delete($name);
|
||||
return Json::success('common.operation.delete.success', []);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/select',
|
||||
summary: '获取已安装插件列表(用于下拉框)',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function select(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
/** @var PluginService $service */
|
||||
$service = Container::make(PluginService::class);
|
||||
|
||||
// 获取本地插件列表(包括已安装和未安装的)
|
||||
$localModules = $service->getLocalModules();
|
||||
|
||||
// 过滤出已安装的插件
|
||||
$installedPlugins = array_filter($localModules, function ($module) {
|
||||
return $module['is_installed'] === true && $module['status'] == 1;
|
||||
});
|
||||
|
||||
// 构建下拉框格式数据
|
||||
$selectItems = [];
|
||||
foreach ($installedPlugins as $plugin) {
|
||||
$selectItems[] = [
|
||||
'value' => $plugin['name'],
|
||||
'label' => $plugin['name'] . ' - ' . $plugin['description'],
|
||||
'name' => $plugin['name'],
|
||||
'description' => $plugin['description'],
|
||||
'version' => $plugin['version'],
|
||||
'type' => $plugin['type'] ?? 'plugin',
|
||||
];
|
||||
}
|
||||
|
||||
return Json::success('common.operation.success', array_values($selectItems));
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), []);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\plugin;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\validate\plugin\PluginValidate;
|
||||
use app\service\admin\plugin\PluginDevelopService;
|
||||
use madong\swagger\annotation\response\DataResponse;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use core\tool\Json;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class PluginDevelopController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(PluginDevelopService $service, PluginValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件列表
|
||||
* 扫描插件目录,获取type为madong:前缀的插件,同步到数据库并返回列表
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \support\Response
|
||||
*/
|
||||
#[OA\Get(
|
||||
path: '/plugin/develop',
|
||||
summary: '插件列表',
|
||||
tags: ['插件开发'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'page',
|
||||
description: '页码',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', default: 1)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'limit',
|
||||
description: '每页数量',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', default: 15)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'title',
|
||||
description: '插件标题',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'author',
|
||||
description: '作者',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'status',
|
||||
description: '状态',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer')
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'plugin:develop:list')]
|
||||
#[PageResponse(schema: [], example: [[
|
||||
"id" => "2032735331152958596",
|
||||
"name" => "官网管理",
|
||||
"icon" => null,
|
||||
"code" => "official",
|
||||
"description" => "官网管理插件,用于搭建和管理企业官方网站",
|
||||
"enabled" => 1,
|
||||
"author" => "Mr.April",
|
||||
"version" => "1.0.0",
|
||||
"cover" => null,
|
||||
"type" => "madong:plugin",
|
||||
"support_app" => "admin",
|
||||
"installed_at" => null,
|
||||
"created_at" => 1773476844,
|
||||
"updated_at" => 1773476844,
|
||||
"variables" => null,
|
||||
"created_date" => "2026-03-14 16:27:24",
|
||||
"updated_date" => "2026-03-14 16:27:24",
|
||||
]])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
// 使用标准模式:通过 selectInput 处理查询参数
|
||||
[$where, , $limit, , , $page] = $this->selectInput($request);
|
||||
|
||||
// 调用服务层获取列表数据
|
||||
$result = $this->service->getList($where, $page, $limit);
|
||||
|
||||
// 使用标准格式返回
|
||||
return Json::success($result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/develop/{id}',
|
||||
summary: '插件详情',
|
||||
tags: ['插件开发'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'id',
|
||||
description: '插件ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'integer')
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'plugin:develop:read')]
|
||||
#[DataResponse(schema: [], example: [
|
||||
"id" => "2032735331152958596",
|
||||
"title" => "官网管理",
|
||||
"icon" => null,
|
||||
"key" => "official",
|
||||
"desc" => "官网管理插件,用于搭建和管理企业官方网站",
|
||||
"status" => 1,
|
||||
"author" => "Mr.April",
|
||||
"version" => "1.0.0",
|
||||
"cover" => null,
|
||||
"type" => "madong:plugin",
|
||||
"support_app" => "admin",
|
||||
"installed_at" => null,
|
||||
"created_at" => 1773476844,
|
||||
"updated_at" => 1773476844,
|
||||
"variables" => null,
|
||||
"created_date" => "2026-03-14 16:27:24",
|
||||
"updated_date" => "2026-03-14 16:27:24",
|
||||
])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$plugin = $this->service->read($id);
|
||||
if (!$plugin) {
|
||||
return Json::fail('插件不存在');
|
||||
}
|
||||
return Json::success($plugin->toArray());
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建插件
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \support\Response
|
||||
* @throws \Throwable
|
||||
*/
|
||||
#[OA\Post(
|
||||
path: '/plugin/develop',
|
||||
summary: '创建插件',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(
|
||||
required: ['title', 'key'],
|
||||
properties: [
|
||||
new OA\Property(property: 'title', description: '插件标题', type: 'string'),
|
||||
new OA\Property(property: 'key', description: '插件标识(唯一值)', type: 'string'),
|
||||
new OA\Property(property: 'desc', description: '插件描述', type: 'string'),
|
||||
new OA\Property(property: 'author', description: '作者', type: 'string'),
|
||||
new OA\Property(property: 'version', description: '版本', type: 'string', default: '1.0.0'),
|
||||
new OA\Property(property: 'type', description: '类型', type: 'string', default: 'madong:plugin'),
|
||||
new OA\Property(property: 'icon', description: '图标(base64)', type: 'string'),
|
||||
new OA\Property(property: 'cover', description: '封面(base64)', type: 'string'),
|
||||
new OA\Property(property: 'frontend_type', description: '前端类型', type: 'string', default: 'admin'),
|
||||
]
|
||||
)
|
||||
),
|
||||
tags: ['插件开发']
|
||||
)]
|
||||
#[Permission(code: 'plugin:develop:create')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$this->validate->scene('store')->check($request->all());
|
||||
$this->service->store($request->all());
|
||||
return Json::success();
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打包插件
|
||||
* 将后端、后台前端、前台前端复制到后端server/plugin/xxx/resource目录,并打包为zip
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \support\Response
|
||||
*/
|
||||
#[OA\Post(
|
||||
path: '/plugin/develop/build',
|
||||
summary: '打包插件',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(
|
||||
required: ['plugin_key'],
|
||||
properties: [
|
||||
new OA\Property(property: 'plugin_key', description: '插件标识', type: 'string'),
|
||||
]
|
||||
)
|
||||
),
|
||||
tags: ['插件开发']
|
||||
)]
|
||||
#[Permission(code: 'plugin:develop:build')]
|
||||
#[SimpleResponse(schema: [], example: [
|
||||
"plugin_key" => "test",
|
||||
"zip_path" => "D:\\MyProject\\private\\madong/server/runtime/adminapi/test.zip",
|
||||
"has_frontend" => false,
|
||||
])]
|
||||
public function build(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$pluginKey = $request->input('plugin_key');
|
||||
if (empty($pluginKey)) {
|
||||
return Json::fail('插件标识不能为空');
|
||||
}
|
||||
|
||||
$result = $this->service->buildPlugin($pluginKey);
|
||||
return Json::success('插件打包成功',$result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/plugin/develop/{id}',
|
||||
summary: '编辑插件',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(property: 'title', description: '插件标题', type: 'string'),
|
||||
new OA\Property(property: 'desc', description: '插件描述', type: 'string'),
|
||||
new OA\Property(property: 'author', description: '作者', type: 'string'),
|
||||
new OA\Property(property: 'version', description: '版本', type: 'string'),
|
||||
new OA\Property(property: 'icon', description: '图标(base64)', type: 'string'),
|
||||
new OA\Property(property: 'cover', description: '封面(base64)', type: 'string'),
|
||||
]
|
||||
)
|
||||
),
|
||||
tags: ['插件开发'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'id',
|
||||
description: '插件ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'integer')
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'plugin:develop:update')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$this->service->update($id, $request->all());
|
||||
return Json::success('更新成功');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\plugin;
|
||||
|
||||
use app\adminapi\controller\Base;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\service\core\plugin\PluginRemoteService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\auth\AllowAnonymous;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class PluginMadongAuthController extends Base
|
||||
{
|
||||
|
||||
public function __construct(PluginRemoteService $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/madong/auth-info',
|
||||
summary: '获取插件授权信息',
|
||||
tags: ['应用授权']
|
||||
)]
|
||||
#[Permission('madong:delegation:read')]
|
||||
#[AllowAnonymous(requireToken: true, requirePermission: true)]
|
||||
#[SimpleResponse(example: '{"code": 0,"msg": "ok","data": {"company_name": "-","domain": "-","auth_code": "202601***********888888"}}')]
|
||||
public function read(Request $request): \support\Response
|
||||
{
|
||||
|
||||
$data = [
|
||||
'company_name' => '-',
|
||||
'domain' => '-',
|
||||
'auth_code' => config('madong.auth_code'),
|
||||
];
|
||||
return Json::success($data);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/madong/auth-info',
|
||||
summary: '设置插件授权信息',
|
||||
tags: ['应用授权']
|
||||
)]
|
||||
#[Permission('madong:delegation:setting')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$param = $request->all();
|
||||
if (empty($param['auth_code'])) {
|
||||
throw new \Exception('auth_code 不能为空');
|
||||
}
|
||||
if (empty($param['auth_secret'])) {
|
||||
throw new \Exception('auth_secret 不能为空');
|
||||
}
|
||||
$this->service->verifyRemoteAuthorization($param['auth_code'],$param['auth_secret']);
|
||||
//追加到系统配置
|
||||
$this->updateMadongConfig($param['auth_code'], $param['auth_secret']);
|
||||
|
||||
return Json::success('授权设置成功');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新server/config/madong.php文件中的配置
|
||||
*
|
||||
* @param string $authCode 授权码
|
||||
* @param string $authSecret 授权密钥
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function updateMadongConfig(string $authCode, string $authSecret): void
|
||||
{
|
||||
$configPath = base_path() . '/config/madong.php';
|
||||
if (!file_exists($configPath)) {
|
||||
throw new \Exception('madong.php配置文件不存在');
|
||||
}
|
||||
|
||||
$configContent = file_get_contents($configPath);
|
||||
|
||||
// 定义需要替换的参数映射
|
||||
$replacements = [
|
||||
'auth_code' => $authCode,
|
||||
'auth_secret' => $authSecret,
|
||||
];
|
||||
|
||||
foreach ($replacements as $key => $value) {
|
||||
$pattern = '/\'' . $key . '\'\s*=>\s*env\(\'madong\..*?\'\s*,\s*\'(.*?)\'\)/';
|
||||
$replacement = "'$key' => env('madong." . ($key === 'auth_code' ? 'code' : 'secret') . "', '$value')";
|
||||
$configContent = preg_replace($pattern, $replacement, $configContent);
|
||||
}
|
||||
|
||||
file_put_contents($configPath, $configContent);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\profile;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\CurrentUser;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\profile\ProfileUpdateRequest;
|
||||
use app\adminapi\schema\request\profile\PasswordUpdateRequest;
|
||||
use app\adminapi\schema\request\profile\AvatarUpdateRequest;
|
||||
use app\adminapi\validate\profile\ProfileValidate;
|
||||
use app\dao\logs\LoginLogDao;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\logs\LoginLogService;
|
||||
use app\service\admin\system\AdminService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\AllowAnonymous;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[OA\Tag(name: '个人中心')]
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class ProfileController extends Crud
|
||||
{
|
||||
public function __construct(AdminService $service, ProfileValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/profile',
|
||||
summary: '获取当前用户信息',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['个人中心']
|
||||
)]
|
||||
#[SimpleResponse(example: '{"id": 1, "user_name": "admin", "email": "test@example.com"}')]
|
||||
#[Permission(code: 'admin:profile:info')]
|
||||
#[AllowAnonymous(requireToken: true, requirePermission: false)]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$userId = Container::make(CurrentUser::class)->id();
|
||||
$data = $this->service->get($userId);
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/profile',
|
||||
summary: '更新个人信息',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['个人中心']
|
||||
)]
|
||||
#[OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(ref: ProfileUpdateRequest::class)
|
||||
)]
|
||||
#[SimpleResponse(example: '{"code": 0, "msg": "success"}')]
|
||||
#[Permission(code: 'admin:profile:update_info')]
|
||||
#[AllowAnonymous(requireToken: true, requirePermission: false)]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$userId = Container::make(CurrentUser::class)->id();
|
||||
$data = $this->inputFilter($request->all());
|
||||
|
||||
// 验证数据
|
||||
if (!$this->validate->scene('update-profile')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
|
||||
$this->service->updateUserInfo($userId, $data);
|
||||
return Json::success('个人信息更新成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/profile/password',
|
||||
summary: '修改密码',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['个人中心']
|
||||
)]
|
||||
#[OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(ref: PasswordUpdateRequest::class)
|
||||
)]
|
||||
#[SimpleResponse(example: '{"code": 0, "msg": "密码修改成功"}')]
|
||||
#[Permission(code: 'admin:profile:password')]
|
||||
#[AllowAnonymous(requireToken: true, requirePermission: false)]
|
||||
public function updatePassword(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$userId = Container::make(CurrentUser::class)->id();
|
||||
$data = $this->inputFilter($request->all(), ['old_password', 'new_password', 'confirm_password']);
|
||||
|
||||
// 验证数据
|
||||
if (!$this->validate->scene('update-password')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
|
||||
$this->service->updateUserPwd($userId, $data);
|
||||
return Json::success('密码修改成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/profile/avatar',
|
||||
summary: '更新头像',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['个人中心']
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
#[Permission(code: 'admin:profile:avatar')]
|
||||
#[AllowAnonymous(requireToken: true, requirePermission: false)]
|
||||
public function updateAvatar(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$userId = Container::make(CurrentUser::class)->id();
|
||||
$avatar = $request->input('avatar');
|
||||
|
||||
// 验证数据
|
||||
if (!$this->validate->scene('update-avatar')->check(['avatar' => $avatar])) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
|
||||
$this->service->updateAvatarUser($userId, $avatar);
|
||||
return Json::success('头像更新成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/profile/sessions',
|
||||
summary: '获取在线设备列表',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['个人中心']
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'page',
|
||||
description: '页码',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', default: 1)
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'limit',
|
||||
description: '每页数量',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', default: 10)
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
#[Permission(code: 'admin:profile:sessions')]
|
||||
#[AllowAnonymous(requireToken: true, requirePermission: false)]
|
||||
public function getSessions(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$page = (int) $request->input('page', 1);
|
||||
$limit = (int) $request->input('limit', 10);
|
||||
|
||||
$currUser = Container::make(CurrentUser::class);
|
||||
$jwt = new \core\jwt\JwtToken();
|
||||
|
||||
// 获取当前用户信息(用于兜底)
|
||||
$adminInfo = $currUser->admin(true) ?? [];
|
||||
$defaultUserName = $adminInfo['user_name'] ?? '';
|
||||
$defaultRealName = $adminInfo['real_name'] ?? '';
|
||||
$defaultAvatar = $adminInfo['avatar'] ?? '';
|
||||
|
||||
// 从 JWT 获取真实的会话列表
|
||||
$allSessions = $jwt->getSessions($currUser->id());
|
||||
$total = count($allSessions);
|
||||
|
||||
// 分页处理
|
||||
$offset = ($page - 1) * $limit;
|
||||
$sessions = array_slice($allSessions, $offset, $limit);
|
||||
|
||||
// 格式化数据
|
||||
$items = array_map(function ($session) use ($defaultUserName, $defaultRealName, $defaultAvatar) {
|
||||
$extra = $session['extra'] ?? [];
|
||||
return [
|
||||
'jti' => $session['jti'],
|
||||
'client_type' => $session['client_type'] ?? 'admin',
|
||||
'login_time' => $session['created_at'] ?? time(),
|
||||
'ip' => $extra['ip'] ?? '',
|
||||
'ip_location'=>$extra['ip_location'] ?? '',
|
||||
'os' => $extra['os'] ?? '',
|
||||
'browser' => $extra['browser'] ?? '',
|
||||
'user_name' => $extra['user_name'] ?? $defaultUserName,
|
||||
'real_name' => $extra['real_name'] ?? $defaultRealName,
|
||||
'avatar' => $extra['avatar'] ?? $defaultAvatar,
|
||||
];
|
||||
}, $sessions);
|
||||
|
||||
return Json::success('ok', compact('total', 'items'));
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Throwable
|
||||
*/
|
||||
#[OA\Delete(
|
||||
path: '/profile/sessions/{jti}',
|
||||
summary: '强制下线指定设备',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['个人中心']
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'jti',
|
||||
description: '会话JTI',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
#[Permission(code: 'admin:profile:kickout')]
|
||||
#[AllowAnonymous(requireToken: true, requirePermission: false)]
|
||||
public function kickoutSession(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$jti = $request->route->param('jti', null);
|
||||
$jwt = new \core\jwt\JwtToken();
|
||||
$result = $jwt->kickoutByJti($jti);
|
||||
|
||||
if (!$result) {
|
||||
return Json::fail('会话不存在或已失效');
|
||||
}
|
||||
|
||||
return Json::success('ok');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/profile/{id}/update-preferences',
|
||||
summary: '更新用户前端偏好设置',
|
||||
tags: ['用户管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(
|
||||
property: 'preferences',
|
||||
description: '偏好设置',
|
||||
type: 'string',
|
||||
example: '{"theme": "dark"}'
|
||||
),
|
||||
]
|
||||
)
|
||||
)]
|
||||
#[Permission(code: 'system:admin:update_preferences')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function updatePreferences(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$uid = Container::make(CurrentUser::class)->id();
|
||||
$data = $request->all();
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
$data['id'] = $uid;
|
||||
if (!$this->validate->scene('update-preferences')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
$this->service->updateUserPreferences($uid, $data);
|
||||
return Json::success('ok');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\review;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\review\ReviewService;
|
||||
use madong\swagger\annotation\response\DataResponse;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use core\tool\Json;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[OA\Tag(name: '审核管理', description: '通用审核模块')]
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class ReviewController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(ReviewService $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/review',
|
||||
summary: '审核列表',
|
||||
tags: ['审核管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "status", description: "审核状态:0待审核,1已通过,2已拒绝", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "reviewable_type", description: "审核类型(morph_map别名)", in: "query", schema: new OA\Schema(type: "string")),
|
||||
new OA\Parameter(name: "page", description: "页码", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "limit", description: "每页数量", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "LIKE_title", description: "标题模糊搜索", in: "query", schema: new OA\Schema(type: "string")),
|
||||
new OA\Parameter(name: "LIKE_applicant", description: "申请人模糊搜索", in: "query", schema: new OA\Schema(type: "string")),
|
||||
new OA\Parameter(name: "LIKE_content", description: "内容模糊搜索", in: "query", schema: new OA\Schema(type: "string")),
|
||||
],
|
||||
)]
|
||||
#[Permission(code: "review:list")]
|
||||
#[PageResponse(schema: [], example: [[
|
||||
'id' => '230104551592014400',
|
||||
'reviewable_type' => 'question',
|
||||
'reviewable_id' => '230104551591014400',
|
||||
'status' => 0,
|
||||
'reason' => '同意',
|
||||
'reviewer_id' => 1,
|
||||
'reviewed_at' => 1758899600,
|
||||
'flow_instance_id' => null,
|
||||
'extra_data' => null,
|
||||
'created_by' => 1,
|
||||
'updated_by' => 1,
|
||||
'created_at' => '2026-03-02T09:12:13.000000Z',
|
||||
'updated_at' => '2026-03-02T09:08:38.000000Z',
|
||||
'title' => '请问Madong框架可以免费商用吗?',
|
||||
'content' => '我想用到项目上可以吗!阿打发斯蒂芬阿斯顿发生大法师',
|
||||
'applicant' => '未知',
|
||||
'reviewer_name' => '超级管理员',
|
||||
'display_name' => 'question',
|
||||
'morph_alias' => null,
|
||||
'status_text' => '待审核',
|
||||
'is_workflow_mode' => false,
|
||||
'reviewer' => [
|
||||
'id' => '1',
|
||||
'user_name' => 'superAdmin',
|
||||
'real_name' => '超级管理员',
|
||||
'nick_name' => '超级管理员',
|
||||
'created_date' => null,
|
||||
'updated_date' => null,
|
||||
],
|
||||
'reviewable' => [
|
||||
'id' => '230104551591014400',
|
||||
'category_id' => '2',
|
||||
'title' => '请问Madong框架可以免费商用吗?',
|
||||
'content' => '我想用到项目上可以吗!阿打发斯蒂芬阿斯顿发生大法师',
|
||||
'deleted_at' => null,
|
||||
'created_at' => '2025-09-26T15:13:20.000000Z',
|
||||
'updated_at' => '2026-03-03T06:39:01.000000Z',
|
||||
'view_count' => 3869,
|
||||
'like_count' => 0,
|
||||
'collect_count' => 0,
|
||||
'comment_count' => 0,
|
||||
'member_id' => '277105476800872448',
|
||||
'is_sticky' => 0,
|
||||
'sticky_order' => 0,
|
||||
'is_excellent' => 0,
|
||||
'is_solved' => 0,
|
||||
'solved_answer_id' => null,
|
||||
'created_date' => '2025-09-26 23:13:20',
|
||||
'updated_date' => '2026-03-03 14:39:01',
|
||||
],
|
||||
]])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
|
||||
// 使用 selectInput 处理参数(现在 title/applicant/content 字段已被跳过)
|
||||
[$where, $format, $limit, $field, $order, $page] = $this->selectInput($request);
|
||||
|
||||
// 准备关联预加载
|
||||
$with = [
|
||||
'reviewer' => function ($query) {
|
||||
$query->select(['id', 'user_name', 'real_name', 'nick_name']);
|
||||
},
|
||||
];
|
||||
|
||||
// 获取映射后的审核列表(支持 extra_data 字段搜索)
|
||||
$items = $this->service->getMappedList($where, $field, $page, $limit, $order, $with, false);
|
||||
|
||||
// 使用支持搜索的计数方法
|
||||
$total = $this->service->getCountWithSearch($where);
|
||||
return $this->formatNormal($items, $total);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/review/{id}',
|
||||
summary: '审核详情',
|
||||
tags: ['审核管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission("review:read")]
|
||||
#[DataResponse(schema: [], example: [
|
||||
'id' => '230104551592014400',
|
||||
'reviewable_type' => 'question',
|
||||
'reviewable_id' => '230104551591014400',
|
||||
'status' => 0,
|
||||
'reason' => '同意',
|
||||
'reviewer_id' => 1,
|
||||
'reviewed_at' => 1758899600,
|
||||
'flow_instance_id' => null,
|
||||
'extra_data' => null,
|
||||
'created_by' => 1,
|
||||
'updated_by' => 1,
|
||||
'created_at' => '2026-03-02T09:12:13.000000Z',
|
||||
'updated_at' => '2026-03-02T09:08:38.000000Z',
|
||||
'title' => '请问Madong框架可以免费商用吗?',
|
||||
'content' => '我想用到项目上可以吗!阿打发斯蒂芬阿斯顿发生大法师',
|
||||
'applicant' => '未知',
|
||||
'reviewer_name' => '超级管理员',
|
||||
'display_name' => 'question',
|
||||
'morph_alias' => null,
|
||||
'status_text' => '待审核',
|
||||
'is_workflow_mode' => false,
|
||||
'reviewer' => [],
|
||||
'reviewable' => [],
|
||||
])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$detail = $this->service->getMappedReview($id);
|
||||
if (!$detail) {
|
||||
return Json::fail('审核记录不存在');
|
||||
}
|
||||
return Json::success('获取成功', $detail);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/review/{id}/approve',
|
||||
summary: '审核通过',
|
||||
tags: ['审核管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "审核ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
)]
|
||||
#[Permission("review:approve")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function approve(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$this->service->approve($id);
|
||||
return Json::success('审核通过');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/review/{id}/reject',
|
||||
summary: '审核拒绝',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: false,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "reason", type: "string", example: "内容违规"),
|
||||
])
|
||||
),
|
||||
tags: ['审核管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "审核ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
)]
|
||||
#[Permission("review:reject")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function reject(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$reason = $request->input('reason', '');
|
||||
$this->service->reject($id, $reason);
|
||||
return Json::success('审核拒绝');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/review/batch/approve',
|
||||
summary: '批量审核通过',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "ids", type: "array", items: new OA\Items(type: "integer"), example: [1, 2, 3]),
|
||||
])
|
||||
),
|
||||
tags: ['审核管理'],
|
||||
)]
|
||||
#[Permission("review:approve")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function batchApprove(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$ids = $request->input('ids', []);
|
||||
$reason = $request->input('reason', '');
|
||||
if (empty($ids)) {
|
||||
return Json::fail('请选择要审核的记录');
|
||||
}
|
||||
$count = $this->service->batchApprove($ids, ['reason' => $reason]);
|
||||
return Json::success('批量审核通过成功', ['count' => $count]);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/review/batch/reject',
|
||||
summary: '批量审核拒绝',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "ids", type: "array", items: new OA\Items(type: "integer"), example: [1, 2, 3]),
|
||||
new OA\Property(property: "reason", type: "string", example: "内容违规"),
|
||||
])
|
||||
),
|
||||
tags: ['审核管理'],
|
||||
)]
|
||||
#[Permission("review:reject")]
|
||||
#[SimpleResponse(schema: [],example: [])]
|
||||
public function batchReject(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$ids = $request->input('ids', []);
|
||||
$reason = $request->input('reason', '');
|
||||
if (empty($ids)) {
|
||||
return Json::fail('请选择要审核的记录');
|
||||
}
|
||||
$count = $this->service->batchReject($ids, $reason, ['reason' => $reason]);
|
||||
return Json::success('批量审核拒绝成功', ['count' => $count]);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/review/review/statistics',
|
||||
summary: '审核统计',
|
||||
tags: ['审核管理'],
|
||||
)]
|
||||
#[Permission("review:list")]
|
||||
#[SimpleResponse(schema: [],example: [])]
|
||||
public function statistics(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$result = $this->service->getStatistics();
|
||||
return Json::success('获取成功', $result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/review/{id}',
|
||||
summary: '删除审核记录',
|
||||
tags: ['审核管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "审核ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
]
|
||||
)]
|
||||
#[Permission("review:delete")]
|
||||
#[SimpleResponse(schema: [],example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,409 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\system;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\system\AdminFormRequest;
|
||||
use app\adminapi\schema\request\system\AdminQueryRequest;
|
||||
use app\adminapi\schema\response\system\AdminResponse;
|
||||
use app\adminapi\validate\system\AdminValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\system\AdminRoleService;
|
||||
use app\service\admin\system\AdminService;
|
||||
use core\exception\handler\AdminException;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class AdminController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(AdminService $service, AdminValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/admin',
|
||||
summary: '列表',
|
||||
tags: ['用户管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => AdminQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:admin:list')]
|
||||
#[PageResponse(schema: AdminResponse::class, example: [
|
||||
'id' => '290809605012324352',
|
||||
'user_name' => 'test11',
|
||||
'real_name' => '123456',
|
||||
'nick_name' => null,
|
||||
'is_super' => 0,
|
||||
'mobile_phone' => '18973598654',
|
||||
'email' => '405784684@qq.com',
|
||||
'avatar' => null,
|
||||
'signed' => null,
|
||||
'dashboard' => null,
|
||||
'dept_id' => null,
|
||||
'enabled' => 1,
|
||||
'login_ip' => null,
|
||||
'login_time' => null,
|
||||
'created_by' => 1,
|
||||
'updated_by' => null,
|
||||
'created_at' => '2026-03-13T03:33:32.000000Z',
|
||||
'updated_at' => '2026-03-13T03:45:28.000000Z',
|
||||
'deleted_at' => null,
|
||||
'sex' => 1,
|
||||
'remark' => 'sadf ',
|
||||
'birthday' => null,
|
||||
'tel' => null,
|
||||
'is_locked' => 0,
|
||||
'created_date' => '2026-03-13 11:33:32',
|
||||
'updated_date' => '2026-03-13 11:45:28',
|
||||
'depts' => [],
|
||||
'posts' => [
|
||||
[
|
||||
'id' => '290793153412726784',
|
||||
'dept_id' => '290786837193555968',
|
||||
'code' => 'tex',
|
||||
'name' => '测试题',
|
||||
'sort' => 1,
|
||||
'enabled' => 1,
|
||||
'created_by' => 1,
|
||||
'updated_by' => null,
|
||||
'created_at' => '2026-03-13T02:28:10.000000Z',
|
||||
'updated_at' => '2026-03-13T02:28:10.000000Z',
|
||||
'deleted_at' => null,
|
||||
'remark' => 'sadv ',
|
||||
'created_date' => '2026-03-13 10:28:10',
|
||||
'updated_date' => '2026-03-13 10:28:10',
|
||||
'pivot' => [
|
||||
'admin_id' => '290809605012324352',
|
||||
'post_id' => '290793153412726784',
|
||||
],
|
||||
],
|
||||
],
|
||||
'roles' => [],
|
||||
])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
[$where, $format, $limit, $field, $order, $page] = $this->selectInput($request);
|
||||
$methods = [
|
||||
'select' => 'formatSelect',
|
||||
'tree' => 'formatTree',
|
||||
'table_tree' => 'formatTableTree',
|
||||
'normal' => 'formatNormal',
|
||||
];
|
||||
$format_function = $methods[$format] ?? 'formatNormal';
|
||||
[$total, $list] = $this->service->getList($where, $field, $page, $limit, $order, [], false);
|
||||
return call_user_func([$this, $format_function], $list, $total);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/system/admin',
|
||||
summary: '插入',
|
||||
tags: ['用户管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => AdminFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:admin:create')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->inputFilter($request->all(), ['post_id_list', 'role_id_list', 'dept_id_list', 'main_dept_id', 'main_post_id']);
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
if (!$this->validate->scene('store')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
$model = $this->service->save($data);
|
||||
if (empty($model)) {
|
||||
throw new AdminException('插入失败');
|
||||
}
|
||||
$pk = $model->getPk();
|
||||
return Json::success('ok', [$pk => $model->getData($pk)]);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/system/admin/{id}',
|
||||
summary: '更新',
|
||||
tags: ['用户管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => AdminFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:admin:update')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $this->inputFilter($request->all(), ['post_id_list', 'role_id_list', 'dept_id_list', 'main_dept_id', 'main_post_id']);
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
if (!$this->validate->scene('update')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
$this->service->update($id, $data);
|
||||
return Json::success('ok');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/admin/{id}',
|
||||
summary: '详情',
|
||||
tags: ['用户管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:admin:read')]
|
||||
#[SimpleResponse(schema: AdminResponse::class, example: [
|
||||
'id' => '290809605012324352',
|
||||
'user_name' => 'test11',
|
||||
'real_name' => '123456',
|
||||
'nick_name' => null,
|
||||
'is_super' => 0,
|
||||
'mobile_phone' => '18973598654',
|
||||
'email' => '405784684@qq.com',
|
||||
'avatar' => null,
|
||||
'signed' => null,
|
||||
'dashboard' => null,
|
||||
'dept_id' => null,
|
||||
'enabled' => 1,
|
||||
'login_ip' => null,
|
||||
'login_time' => null,
|
||||
'created_by' => 1,
|
||||
'updated_by' => null,
|
||||
'created_at' => '2026-03-13T03:33:32.000000Z',
|
||||
'updated_at' => '2026-03-13T03:45:28.000000Z',
|
||||
'deleted_at' => null,
|
||||
'sex' => 1,
|
||||
'remark' => 'sadf ',
|
||||
'birthday' => null,
|
||||
'tel' => null,
|
||||
'is_locked' => 0,
|
||||
'created_date' => '2026-03-13 11:33:32',
|
||||
'updated_date' => '2026-03-13 11:45:28',
|
||||
'depts' => [],
|
||||
'posts' => [
|
||||
[
|
||||
'id' => '290793153412726784',
|
||||
'dept_id' => '290786837193555968',
|
||||
'code' => 'tex',
|
||||
'name' => '测试题',
|
||||
'sort' => 1,
|
||||
'enabled' => 1,
|
||||
'created_by' => 1,
|
||||
'updated_by' => null,
|
||||
'created_at' => '2026-03-13T02:28:10.000000Z',
|
||||
'updated_at' => '2026-03-13T02:28:10.000000Z',
|
||||
'deleted_at' => null,
|
||||
'remark' => 'sadv ',
|
||||
'created_date' => '2026-03-13 10:28:10',
|
||||
'updated_date' => '2026-03-13 10:28:10',
|
||||
'pivot' => [
|
||||
'admin_id' => '290809605012324352',
|
||||
'post_id' => '290793153412726784',
|
||||
],
|
||||
],
|
||||
],
|
||||
'roles' => [],
|
||||
])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$result = $this->service->getAdminById($id)->toArray();
|
||||
return Json::success('ok', $result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/system/admin/{id}/locked',
|
||||
summary: '禁用',
|
||||
tags: ['用户管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:admin:locked')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function locked(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
// 获取单个 ID 和多个 ID
|
||||
$data = $request->input('data', []);
|
||||
$id = $request->input('id');
|
||||
|
||||
// 如果提供了多个 ID,优先使用多个 ID
|
||||
if (!empty($data)) {
|
||||
$id = $data;
|
||||
} else if (empty($id)) {
|
||||
return Json::fail('Either id or data must be provided.');
|
||||
}
|
||||
$this->service->locked($id);
|
||||
return Json::success('ok');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/system/admin/{id}/unlocked',
|
||||
summary: '解除',
|
||||
tags: ['用户管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:admin:unlocked')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function unLocked(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
// 获取单个 ID 和多个 ID
|
||||
$data = $request->input('data', []);
|
||||
$id = $request->input('id');
|
||||
|
||||
// 如果提供了多个 ID,优先使用多个 ID
|
||||
if (!empty($data)) {
|
||||
$id = $data;
|
||||
} else if (empty($id)) {
|
||||
return Json::fail('Either id or data must be provided.');
|
||||
}
|
||||
$this->service->unLocked($id);
|
||||
return Json::success('ok');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/system/admin/{id}/change-password',
|
||||
summary: '重置密码',
|
||||
tags: ['用户管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:admin:change_password')]
|
||||
#[SimpleResponse(example: '{"code": 0,"msg": "success"}')]
|
||||
public function changePassword(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$ids = $request->input('ids');
|
||||
$password = $request->input('password', 123456);
|
||||
$data = ['password' => password_hash($password, PASSWORD_DEFAULT)];
|
||||
$this->service->batchUpdate(['id' => $ids], $data);
|
||||
return Json::success('ok');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/system/admin/grant-role',
|
||||
summary: '授权角色',
|
||||
tags: ['用户管理'],
|
||||
)]
|
||||
#[Permission(code: 'system:admin:grant_role')]
|
||||
#[SimpleResponse(schema: [], example: '{"code": 0,"msg": "ok"}')]
|
||||
public function grantRole(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->inputFilter($request->all(), ['id', 'role_id_list']);
|
||||
/** @var AdminRoleService $systemUserRoleService */
|
||||
$systemUserRoleService = Container::make(AdminRoleService::class);
|
||||
$systemUserRoleService->save($data);
|
||||
return Json::success('ok');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/system/admin/{id}',
|
||||
summary: '删除',
|
||||
tags: ['用户管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:admin:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
|
||||
try {
|
||||
$data = $this->getDeleteIds($request);
|
||||
$this->service->batchDelete($data);
|
||||
return Json::success('ok');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/system/admin',
|
||||
summary: '批量删除',
|
||||
tags: ['用户管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:admin:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->getDeleteIds($request);
|
||||
$this->service->batchDelete($data);
|
||||
return Json::success('ok');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\controller\system;
|
||||
|
||||
use app\adminapi\controller\Base;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\attribute\SkipApiEncrypt;
|
||||
use app\service\admin\system\ConfigService;
|
||||
use core\tool\ApiEncryptService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\DataResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
#[SkipApiEncrypt]
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class ApiEncryptController extends Base
|
||||
{
|
||||
protected ApiEncryptService $encryptService;
|
||||
|
||||
protected ConfigService $configService;
|
||||
|
||||
public function __construct(ApiEncryptService $encryptService, ConfigService $configService)
|
||||
{
|
||||
$this->encryptService = $encryptService;
|
||||
$this->configService = $configService;
|
||||
}
|
||||
|
||||
#[OA\Get(path: '/system/api-encrypt', summary: '获取接口加密配置', tags: ['系统配置'])]
|
||||
#[Permission(code: 'system:config:update')]
|
||||
#[DataResponse(description: '获取成功')]
|
||||
public function detail(Request $request): Response
|
||||
{
|
||||
$config = $this->configService->config('api_encrypt_setting', [], ['group_code' => 'system']);
|
||||
$config = is_array($config) ? $config : [];
|
||||
|
||||
return Json::success('获取成功', [
|
||||
'enabled' => (int) ($config['enabled'] ?? ($this->encryptService->isEnabled() ? 1 : 0)),
|
||||
'scope' => (string) ($config['scope'] ?? $this->encryptService->getScope()),
|
||||
'key_version' => (string) ($config['key_version'] ?? $this->encryptService->getKeyVersion()),
|
||||
'timestamp_tolerance' => (int) ($config['timestamp_tolerance'] ?? 300),
|
||||
'exclude_paths' => $config['exclude_paths'] ?? [],
|
||||
'encrypted_paths' => $config['encrypted_paths'] ?? $this->encryptService->getEncryptedPaths(),
|
||||
'public_key_path' => (string) ($config['public_key_path'] ?? ''),
|
||||
'private_key_path' => (string) ($config['private_key_path'] ?? ''),
|
||||
'public_key' => $this->encryptService->getPublicKey(),
|
||||
'private_key' => $this->encryptService->getPrivateKey(),
|
||||
]);
|
||||
}
|
||||
|
||||
#[OA\Post(path: '/system/api-encrypt/generate-keypair', summary: '生成接口加密密钥对', tags: ['系统配置'])]
|
||||
#[Permission(code: 'system:config:update')]
|
||||
#[DataResponse(description: '生成成功')]
|
||||
public function generateKeypair(Request $request): Response
|
||||
{
|
||||
try {
|
||||
$keys = $this->encryptService->generateKeyPair();
|
||||
$this->encryptService->persistKeyPair($keys);
|
||||
|
||||
return Json::success('生成成功', [
|
||||
'public_key' => $this->encryptService->getPublicKey(),
|
||||
'private_key' => $this->encryptService->getPrivateKey(),
|
||||
]);
|
||||
} catch (\Throwable $exception) {
|
||||
return Json::fail($exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\system;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\CurrentUser;
|
||||
use app\adminapi\event\MenuFormattingEvent;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use support\Container;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\validate\system\AuthValidate;
|
||||
use app\service\admin\system\AdminRoleService;
|
||||
use app\service\admin\system\AdminService;
|
||||
use app\service\admin\system\AuthService;
|
||||
use app\service\admin\system\MenuService;
|
||||
use app\service\admin\system\RoleMenuService;
|
||||
use app\service\admin\system\RoleScopeDeptService;
|
||||
use app\service\admin\system\RoleService;
|
||||
use madong\swagger\attribute\AllowAnonymous;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use core\exception\handler\UnauthorizedHttpException;
|
||||
use core\jwt\JwtToken;
|
||||
use core\tool\Json;
|
||||
use madong\helper\Dict;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use Webman\Event\Event;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class AuthController extends Crud
|
||||
{
|
||||
public function __construct(AuthService $service, AuthValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/auth/user-info',
|
||||
summary: '获取登录用户',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关'],
|
||||
)]
|
||||
#[Permission(code: 'system:auth:user_info')]
|
||||
#[AllowAnonymous(requireToken: true, requirePermission: false)]
|
||||
#[SimpleResponse(example: '{"code": 0,"msg": "ok","data": {"id": "1","user_name": "admin","real_name": "超级管理员","nick_name": "超级管理员","is_super": 1,"mobile_phone": null,"email": null,"avatar": null,"signed": null,"dashboard": null,"dept_id": "","enabled": 1,"login_ip": "127.0.0.1","login_time": 1762429533,"created_by": null,"updated_by": null,"created_at": "2025-10-30T12:24:09.000000Z","updated_at": "2025-11-06T11:45:34.000000Z","deleted_at": null,"sex": 0,"remark": null,"birthday": null,"tel": null,"is_locked": 0,"role_id_list": [],"post_id_list": [],"created_date": "2025-10-30 20:24:09","updated_date": "2025-11-06 19:45:34","depts": [],"posts": [],"casbin": [],"roles": []}}')]
|
||||
public function getUserInfo(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
/** @var CurrentUser $currentUser */
|
||||
$currentUser = Container::make(CurrentUser::class);
|
||||
$data = $currentUser->admin(true);
|
||||
if (empty($data)) {
|
||||
throw new UnauthorizedHttpException('用户凭证失效请重新登录');
|
||||
}
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), null, 401);
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/auth/user-menus',
|
||||
summary: '获取用户-权限菜单',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关'],
|
||||
)]
|
||||
#[Permission(code: 'system:auth:user_menus')]
|
||||
#[AllowAnonymous(requireToken: true, requirePermission: false)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function getPermissionsMenu(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$format = input('format', 'default');
|
||||
/** @var CurrentUser $currUser */
|
||||
$currUser = Container::make(CurrentUser::class);
|
||||
$collection = $this->service->getMenusByUserRoles($currUser, true);
|
||||
$event = new MenuFormattingEvent($collection, $format);
|
||||
$data = $event->dispatch();
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/auth/user-permissions',
|
||||
summary: '权限菜单-包含所有 菜单 按钮 接口',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关'],
|
||||
)]
|
||||
#[Permission(code: 'system:auth:user_permissions')]
|
||||
#[SimpleResponse(example: '{"code": 0,"msg": "ok","data": []}')]
|
||||
public function getPermissions(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$format = input('format', 'default');
|
||||
/** @var CurrentUser $currUser */
|
||||
$currUser = Container::make(CurrentUser::class);
|
||||
$collection = $this->service->getMenusByUserRoles($currUser, true);
|
||||
$event = new MenuFormattingEvent($collection, $format);
|
||||
$data = $event->dispatch();
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/auth/perm-code',
|
||||
summary: '获取用户-权限码',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关'],
|
||||
)]
|
||||
#[Permission(code: 'system:auth:perm_code')]
|
||||
#[AllowAnonymous(requireToken: true, requirePermission: false)]
|
||||
#[SimpleResponse(example: '{"code": 0,"msg": "ok","data": ["admin"]}')]
|
||||
public function getUserCodes(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
/** @var CurrentUser $currUser */
|
||||
$currUser = Container::make(CurrentUser::class);
|
||||
$data = $this->service->getCodesByUserRoles($currUser);
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/auth/role-menu-ids',
|
||||
summary: '通过角色ID获取权限ID集合',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关'],
|
||||
)]
|
||||
#[Permission(code: 'system:auth:role_menu_ids')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function roleMenuIds(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$roleId = $request->input('role_id');
|
||||
/** @var RoleService $systemRoleService */
|
||||
$systemRoleService = Container::make(RoleService::class);
|
||||
$data = $systemRoleService->getPermissionColumns($roleId);
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/auth/role-scope-ids',
|
||||
summary: '通过角色ID获取权限范围自定义部门ID集合',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关'],
|
||||
)]
|
||||
#[Permission(code: 'system:auth:role_scope_ids')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function roleScopeIds(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$roleId = $request->input('role_id');
|
||||
/** @var RoleScopeDeptService $systemRoleMenuService */
|
||||
$systemRoleMenuService = Container::make(RoleScopeDeptService::class);
|
||||
$data = $systemRoleMenuService->getColumn(['role_id' => $roleId], 'dept_id');
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/system/auth/save-role-menu',
|
||||
summary: '保存角色菜单关系',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关'],
|
||||
)]
|
||||
#[Permission(code: 'system:auth:role_menu')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function saveRoleMenuRelation(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->all();
|
||||
/** @var RoleMenuService $systemRoleMenuService */
|
||||
$systemRoleMenuService = Container::make(RoleMenuService::class);
|
||||
$systemRoleMenuService->save($data);
|
||||
return Json::success('success');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/auth/user-list-by-role-id',
|
||||
summary: '获取角色-关联用户列表',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'role_id',
|
||||
description: '角色ID',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string'),
|
||||
)]
|
||||
#[Permission(code: 'system:auth:role_user_list')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function getUsersByRoleId(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
[$where, $format, $limit, $field, $order, $page] = $this->selectInput($request, [
|
||||
'exclude_filters' => true,
|
||||
'filter_format' => 'simple', // 简洁模式没有操作符
|
||||
'custom_fields' => [
|
||||
'user_name',
|
||||
'real_name',
|
||||
'mobile_phone',
|
||||
'role_id',
|
||||
],
|
||||
]);
|
||||
/** @var AdminService $systemUserService */
|
||||
$systemUserService = Container::make(AdminService::class);
|
||||
$data = $systemUserService->getUsersListByRoleId($where, $field, $page, $limit);
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/system/auth/save-user-role',
|
||||
summary: '保存用户-关联角色',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关'],
|
||||
)]
|
||||
#[Permission(code: 'system:auth:save_user_role')]
|
||||
#[SimpleResponse(example: '{"code": 0,"msg": "success"}')]
|
||||
public function saveUserRoles(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->all();
|
||||
/** @var AdminRoleService $systemUserRoleService */
|
||||
$systemUserRoleService = Container::make(AdminRoleService::class);
|
||||
$systemUserRoleService->saveUserRoles($data);
|
||||
return Json::success('success');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/system/auth/remove-user-role',
|
||||
summary: '移除用户-关联角色',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关'],
|
||||
)]
|
||||
#[Permission(code: 'system:auth:remove_user_role')]
|
||||
#[SimpleResponse(example: '{"code": 0,"msg": "ok"}')]
|
||||
public function removeUserRole(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->all();
|
||||
/** @var AdminRoleService $systemUserRoleService */
|
||||
$systemUserRoleService = Container::make(AdminRoleService::class);
|
||||
$systemUserRoleService->removeUserRole($data);
|
||||
return Json::success('ok');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/auth/user-list-exclude-role-id',
|
||||
summary: '获取用户列表-排除指定角色id',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'role_id',
|
||||
description: '角色ID',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string'),
|
||||
)]
|
||||
#[Permission(code: 'system:auth:user_list_exclude_role')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function getUsersExcludingRole(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
[$where, $format, $limit, $field, $order, $page] = $this->selectInput($request, [
|
||||
'exclude_filters' => true,
|
||||
'filter_format' => 'simple', // 简洁模式没有操作符
|
||||
'custom_fields' => [
|
||||
'LIKE_user_name',
|
||||
'LIKE_real_name',
|
||||
'LIKE_mobile_phone',
|
||||
'role_id',
|
||||
]]);
|
||||
$systemUserService = Container::make(AdminService::class);
|
||||
$data = $systemUserService->getUsersExcludingRole($where, $field, $page, $limit);
|
||||
return Json::success('ok', $data);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \core\jwt\ex\JwtRefreshTokenExpiredException
|
||||
*/
|
||||
#[OA\Post(
|
||||
path: '/system/auth/refresh-token',
|
||||
summary: '刷新Token',
|
||||
security: [['Bearer' => [], 'ApiKey' => []]],
|
||||
tags: ['权限相关'],
|
||||
)]
|
||||
#[Permission(code: 'system:auth:refresh_token')]
|
||||
#[AllowAnonymous(requireToken: false, requirePermission: false)]
|
||||
#[SimpleResponse(example: '{"data": "new_access_token"}')]
|
||||
public function refreshToken(Request $request): \support\Response
|
||||
{
|
||||
$token = (new JwtToken())->refresh();
|
||||
return Json::success('ok', [
|
||||
'access_token' => $token->accessToken,
|
||||
'refresh_token' => $token->refreshToken,
|
||||
'expires_in' => $token->expiresIn,
|
||||
'expires_at' => $token->expiresAt->getTimestamp(),
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,579 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\system;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\system\ConfigGroupQueryRequest;
|
||||
use app\adminapi\schema\request\system\ConfigQueryRequest;
|
||||
use app\adminapi\schema\request\system\ConfigValueRequest;
|
||||
use app\service\admin\system\ConfigService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\AllowAnonymous;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class ConfigController extends Crud
|
||||
{
|
||||
public function __construct(ConfigService $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
|
||||
#[OA\Get(
|
||||
path: "/system/config/group/{group_code}",
|
||||
summary: "按分组获取配置",
|
||||
tags: ["系统配置"],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => ConfigGroupQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[AllowAnonymous(requireToken: false, requirePermission: false)]
|
||||
#[SimpleResponse(schema: [], example: '{"site_open": "1","site_url": "http://127.0.0.1:8001","site_name": "madong-admin","site_logo": "https://madong.tech/assets/images/logo.svg","site_network_security": "2024042441号-2","site_description": "快速开发框架","site_record_no": "2024042442","site_icp_url": "https://beian.miit.gov.cn/","site_network_security_url": ""}')]
|
||||
public function getByGroup(Request $request, string $group_code): \support\Response
|
||||
{
|
||||
try {
|
||||
// 参数验证
|
||||
if (empty($group_code)) {
|
||||
return Json::fail('分组编码不能为空');
|
||||
}
|
||||
|
||||
$options = [
|
||||
'enabled_only' => $request->input('enabled_only', true),
|
||||
'with_metadata' => $request->input('with_metadata', false),
|
||||
];
|
||||
$result = $this->service->getByGroup($group_code, [], $options);
|
||||
return Json::success('操作成功', $result);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: "/system/config/code/{code}",
|
||||
summary: "按code获取配置",
|
||||
tags: ["系统配置"],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => ConfigQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "code",
|
||||
description: "配置编码",
|
||||
in: "path",
|
||||
required: true,
|
||||
schema: new OA\Schema(type: "string"),
|
||||
)]
|
||||
#[AllowAnonymous(requireToken: false, requirePermission: false)]
|
||||
#[SimpleResponse(schema: [], example: ' {"site_open": "1","site_url": "http://127.0.0.1:8001"}')]
|
||||
public function getByCode(Request $request, string $code): \support\Response
|
||||
{
|
||||
try {
|
||||
// 参数验证
|
||||
if (empty($code)) {
|
||||
return Json::fail('配置编码不能为空');
|
||||
}
|
||||
|
||||
$groupCode = $request->input('group_code', '');
|
||||
$options = [];
|
||||
if (!empty($groupCode)) {
|
||||
$options['group_code'] = $groupCode;
|
||||
}
|
||||
$result = $this->service->config($code, [], $options);
|
||||
return Json::success('操作成功', $result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存单个配置
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \support\Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
#[OA\Post(
|
||||
path: "/system/config",
|
||||
summary: "保存单个配置",
|
||||
tags: ["系统配置"],
|
||||
)]
|
||||
#[Permission(code: 'system:config:create')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->all();
|
||||
|
||||
// 参数验证
|
||||
if (empty($data['group_code']) || empty($data['code']) || empty($data['name'])) {
|
||||
return Json::fail('参数不能为空');
|
||||
}
|
||||
|
||||
$options = [
|
||||
'group_code' => $data['group_code'],
|
||||
'name' => $data['name'],
|
||||
];
|
||||
|
||||
if (isset($data['enabled'])) {
|
||||
$options['enabled'] = $data['enabled'];
|
||||
}
|
||||
|
||||
$this->service->update(
|
||||
$data['code'],
|
||||
$data['content'],
|
||||
$options
|
||||
);
|
||||
|
||||
return Json::success('保存成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存单个配置
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \support\Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
#[OA\Put(
|
||||
path: "/system/config/{code}",
|
||||
summary: "更新配置",
|
||||
tags: ["系统配置"],
|
||||
)]
|
||||
#[Permission(code: 'system:config:update')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->all();
|
||||
$code = $request->route->param('code');
|
||||
|
||||
$options = [
|
||||
'name' => $data['name'],
|
||||
'group_code' => $data['group_code'] ?? '',
|
||||
'enabled' => $data['enabled'] ?? 0,
|
||||
];
|
||||
$this->service->update(
|
||||
$code,
|
||||
$data['content'],
|
||||
$options
|
||||
);
|
||||
|
||||
return Json::success('更新成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取特定配置项的特定键值
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $code
|
||||
*
|
||||
* @return \support\Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
#[OA\Get(
|
||||
path: "/system/config/{code}/value",
|
||||
summary: "获取特定配置项的特定键值",
|
||||
tags: ["系统配置"],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => ConfigValueRequest::class,
|
||||
]
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: '{"data": "value"}')]
|
||||
public function getValue(Request $request, string $code): \support\Response
|
||||
{
|
||||
try {
|
||||
// 参数验证
|
||||
if (empty($code)) {
|
||||
return Json::fail('配置项编码不能为空');
|
||||
}
|
||||
|
||||
$groupCode = $request->input('group_code');
|
||||
$key = $request->input('key');
|
||||
|
||||
if (empty($groupCode) || empty($key)) {
|
||||
return Json::fail('分组编码和配置键不能为空');
|
||||
}
|
||||
|
||||
$result = $this->service->getValue($code, $key, null, ['group_code' => $groupCode]);
|
||||
return Json::success('操作成功', $result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[OA\Put(
|
||||
path: "/system/config/{code}/value",
|
||||
summary: "更新特定配置项的特定键值",
|
||||
tags: ["系统配置"],
|
||||
)]
|
||||
#[Permission(code: 'system:config:update_value')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function setValue(Request $request, string $code): \support\Response
|
||||
{
|
||||
try {
|
||||
// 参数验证
|
||||
if (empty($code)) {
|
||||
return Json::fail('配置项编码不能为空');
|
||||
}
|
||||
|
||||
$groupCode = $request->post('group_code');
|
||||
$key = $request->post('key');
|
||||
$value = $request->post('value');
|
||||
|
||||
if (empty($groupCode) || empty($key)) {
|
||||
return Json::fail('分组编码和配置键不能为空');
|
||||
}
|
||||
|
||||
$result = $this->service->setValue($code, $key, $value, ['group_code' => $groupCode]);
|
||||
if ($result) {
|
||||
return Json::success('更新成功');
|
||||
} else {
|
||||
return Json::fail('更新失败');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[OA\Get(
|
||||
path: "/system/config/all-grouped",
|
||||
summary: "获取所有配置并按分组整理",
|
||||
tags: ["系统配置"],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "enabled_only",
|
||||
description: "是否只获取启用的配置项",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: new OA\Schema(type: "boolean", default: true),
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "group_filter",
|
||||
description: "分组过滤,多个分组用逗号分隔",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: new OA\Schema(type: "string"),
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "with_metadata",
|
||||
description: "是否包含配置项的完整元数据",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: new OA\Schema(type: "boolean", default: false),
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "key_by",
|
||||
description: "按指定字段作为键名('code' 或 'id')",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: new OA\Schema(type: "string", enum: ["code", "id"]),
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "sort_groups",
|
||||
description: "是否对分组进行排序",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: new OA\Schema(type: "boolean", default: false),
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "sort_configs",
|
||||
description: "是否对配置项进行排序",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: new OA\Schema(type: "boolean", default: false),
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: '{"site": {"site_name": "网站名称"}, "oss": {"accessKeyId": "xxx"}}')]
|
||||
public function getAllGrouped(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$options = [
|
||||
'enabled_only' => $request->input('enabled_only', true),
|
||||
'with_metadata' => $request->input('with_metadata', false),
|
||||
'key_by' => $request->input('key_by', null),
|
||||
'sort_groups' => $request->input('sort_groups', false),
|
||||
'sort_configs' => $request->input('sort_configs', false),
|
||||
];
|
||||
|
||||
// 处理分组过滤参数
|
||||
$groupFilter = $request->input('group_filter', '');
|
||||
if (!empty($groupFilter)) {
|
||||
$options['group_filter'] = explode(',', $groupFilter);
|
||||
}
|
||||
|
||||
$result = $this->service->getAllGrouped($options);
|
||||
return Json::success('操作成功', $result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[OA\Get(
|
||||
path: "/system/config/items",
|
||||
summary: "获取配置项列表",
|
||||
tags: ["系统配置"],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "page",
|
||||
description: "页码",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: new OA\Schema(type: "integer", default: 1),
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "pageSize",
|
||||
description: "每页数量",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: new OA\Schema(type: "integer", default: 20),
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "groupCode",
|
||||
description: "分组代码",
|
||||
in: "query",
|
||||
required: true,
|
||||
schema: new OA\Schema(type: "string"),
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "keyword",
|
||||
description: "搜索关键字",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: new OA\Schema(type: "string"),
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: ['items'=>[],'total'=>0])]
|
||||
public function getItems(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$page = $request->input('page', 1);
|
||||
$pageSize = $request->input('pageSize', 20);
|
||||
$groupCode = $request->input('groupCode', '');
|
||||
$keyword = $request->input('keyword', '');
|
||||
|
||||
if (empty($groupCode)) {
|
||||
return Json::fail('分组代码不能为空');
|
||||
}
|
||||
|
||||
$result = $this->service->getItems((int)$page, (int)$pageSize, $groupCode, $keyword);
|
||||
return Json::success('操作成功', $result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建单个配置项
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \support\Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
#[OA\Post(
|
||||
path: "/system/config/item",
|
||||
summary: "创建配置项",
|
||||
tags: ["系统配置"],
|
||||
)]
|
||||
#[Permission(code: 'system:config:create')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function createItem(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->post();
|
||||
|
||||
// 参数验证
|
||||
if (empty($data['group_code']) || empty($data['code']) || empty($data['name']) || !isset($data['value'])) {
|
||||
return Json::fail('参数不能为空');
|
||||
}
|
||||
|
||||
$type = $data['type'] ?? 'string';
|
||||
|
||||
// 值类型转换
|
||||
$value = $this->convertValue($data['value'], $type);
|
||||
|
||||
$configData = [
|
||||
'group_code' => $data['group_code'],
|
||||
'code' => $data['code'],
|
||||
'name' => $data['name'],
|
||||
'content' => $type === 'json' ? $value : (string)$value,
|
||||
'type' => $type,
|
||||
'description' => $data['description'] ?? '',
|
||||
'enabled' => $data['enabled'] ?? 1,
|
||||
'is_sys' => 0,
|
||||
'remark' => '',
|
||||
];
|
||||
|
||||
$this->service->createItem($configData);
|
||||
|
||||
return Json::success('创建成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新单个配置项
|
||||
*
|
||||
* @param Request $request
|
||||
* @param int $id
|
||||
*
|
||||
* @return \support\Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
#[OA\Put(
|
||||
path: "/system/config/item/{id}",
|
||||
summary: "更新配置项",
|
||||
tags: ["系统配置"],
|
||||
)]
|
||||
#[Permission(code: 'system:config:update')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function updateItem(Request $request, int $id): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $request->post();
|
||||
|
||||
$type = $data['type'] ?? 'string';
|
||||
|
||||
// 值类型转换
|
||||
$value = $this->convertValue($data['value'], $type);
|
||||
|
||||
$configData = [
|
||||
'name' => $data['name'],
|
||||
'content' => $type === 'json' ? $value : (string)$value,
|
||||
'type' => $type,
|
||||
'description' => $data['description'] ?? '',
|
||||
'enabled' => $data['enabled'] ?? 1,
|
||||
];
|
||||
|
||||
$this->service->updateItem($id, $configData);
|
||||
|
||||
return Json::success('更新成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[OA\Put(
|
||||
path: "/system/config/item/{id}/toggle",
|
||||
summary: "切换配置项状态",
|
||||
tags: ["系统配置"],
|
||||
)]
|
||||
#[Permission(code: 'system:config:update')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function toggleItem(Request $request, int $id): \support\Response
|
||||
{
|
||||
try {
|
||||
$enabled = $request->post('enabled', 0);
|
||||
$this->service->toggleItem($id, (bool)$enabled);
|
||||
return Json::success('操作成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 值类型转换
|
||||
*
|
||||
* @param mixed $value 原始值
|
||||
* @param string $type 目标类型
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function convertValue(mixed $value, string $type): mixed
|
||||
{
|
||||
return match ($type) {
|
||||
'string' => (string)$value,
|
||||
'number' => (string)$value,
|
||||
'boolean' => $value ? '1' : '0',
|
||||
'json' => is_string($value) ? $value : json_encode($value, JSON_UNESCAPED_UNICODE),
|
||||
default => (string)$value
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除配置项(按code)
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $code 配置编码
|
||||
*
|
||||
* @return \support\Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
#[OA\Delete(
|
||||
path: "/system/config/{code}",
|
||||
summary: "删除配置项",
|
||||
tags: ["系统配置"],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "code",
|
||||
description: "配置编码",
|
||||
in: "path",
|
||||
required: true,
|
||||
schema: new OA\Schema(type: "string"),
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "group_code",
|
||||
description: "分组代码",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: new OA\Schema(type: "string"),
|
||||
)]
|
||||
#[Permission(code: 'system:config:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function delete(Request $request, string $code): \support\Response
|
||||
{
|
||||
try {
|
||||
if (empty($code)) {
|
||||
return Json::fail('配置编码不能为空');
|
||||
}
|
||||
|
||||
$options = [];
|
||||
$groupCode = $request->input('group_code');
|
||||
if (!empty($groupCode)) {
|
||||
$options['group_code'] = $groupCode;
|
||||
}
|
||||
|
||||
$result = $this->service->delete($code, $options);
|
||||
if ($result) {
|
||||
return Json::success('删除成功');
|
||||
} else {
|
||||
return Json::fail('配置不存在');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\controller\system;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\system\FeedbackFormRequest;
|
||||
use app\adminapi\schema\request\system\FeedbackQueryRequest;
|
||||
use app\adminapi\schema\response\system\FeedbackResponse;
|
||||
use app\adminapi\validate\system\FeedbackValidate;
|
||||
use app\adminapi\CurrentUser;
|
||||
use core\tool\Json;
|
||||
use support\Container;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\system\FeedbackService;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class FeedbackController extends Crud
|
||||
{
|
||||
public function __construct(FeedbackService $service, FeedbackValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(path: '/feedback', summary: '问题反馈列表', tags: ['问题反馈'], x: [SchemaConstants::X_SCHEMA_REQUEST => FeedbackQueryRequest::class])]
|
||||
#[Permission(code: 'setting:feedback:list')]
|
||||
#[PageResponse(schema: FeedbackResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Get(path: '/feedback/{id}', summary: '问题反馈详情', tags: ['问题反馈'], x: [SchemaConstants::X_PROPERTY_IN => 'id', SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class])]
|
||||
#[Permission(code: 'setting:feedback:read')]
|
||||
#[SimpleResponse(schema: FeedbackResponse::class, example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Post(path: '/feedback', summary: '创建问题反馈', tags: ['问题反馈'], x: [SchemaConstants::X_SCHEMA_REQUEST => FeedbackFormRequest::class])]
|
||||
#[Permission(code: 'setting:feedback:create')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
return parent::store($request);
|
||||
}
|
||||
|
||||
#[OA\Put(path: '/feedback/{id}', summary: '更新问题反馈', tags: ['问题反馈'], x: [SchemaConstants::X_SCHEMA_REQUEST => FeedbackFormRequest::class])]
|
||||
#[OA\Parameter(name: 'id', description: '反馈ID', in: 'path', required: true, schema: new OA\Schema(type: 'string'))]
|
||||
#[Permission(code: 'setting:feedback:update')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
return parent::update($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/feedback/{id}/reply',
|
||||
summary: '回复问题反馈',
|
||||
tags: ['问题反馈'],
|
||||
parameters: [new OA\Parameter(name: 'id', description: '反馈ID', in: 'path', required: true, schema: new OA\Schema(type: 'string'))],
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(
|
||||
required: ['reply_content'],
|
||||
properties: [new OA\Property(property: 'reply_content', description: '回复内容', type: 'string', maxLength: 2000)]
|
||||
)
|
||||
)
|
||||
)]
|
||||
#[Permission(code: 'setting:feedback:reply')]
|
||||
#[SimpleResponse(schema: FeedbackResponse::class, example: ['code' => 0, 'message' => '回复成功', 'data' => []])]
|
||||
public function reply(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = [
|
||||
'id' => $request->route->param('id'),
|
||||
'reply_content' => trim((string)$request->input('reply_content')),
|
||||
];
|
||||
$this->validate->scene('reply')->check($data);
|
||||
if ($data['reply_content'] === '') {
|
||||
return Json::fail('请填写回复内容');
|
||||
}
|
||||
|
||||
$feedback = $this->service->get($data['id']);
|
||||
if (!$feedback) {
|
||||
return Json::fail('反馈记录不存在');
|
||||
}
|
||||
|
||||
$adminId = Container::make(CurrentUser::class)->id();
|
||||
if (!$adminId) {
|
||||
return Json::fail('登录状态已失效');
|
||||
}
|
||||
|
||||
$this->service->update($data['id'], [
|
||||
'reply_content' => $data['reply_content'],
|
||||
'reply_by' => $adminId,
|
||||
'reply_at' => time(),
|
||||
]);
|
||||
|
||||
return Json::success('回复成功', $this->service->get($data['id'])?->toArray());
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(path: '/feedback/{id}', summary: '删除问题反馈', tags: ['问题反馈'], x: [SchemaConstants::X_PROPERTY_IN => 'id', SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class])]
|
||||
#[Permission(code: 'setting:feedback:delete')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(path: '/feedback', summary: '批量删除问题反馈', tags: ['问题反馈'], x: [SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class])]
|
||||
#[Permission(code: 'setting:feedback:delete')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\system;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\system\MenuFormRequest;
|
||||
use app\adminapi\schema\request\system\MenuQueryRequest;
|
||||
use app\adminapi\schema\response\system\MenuResponse;
|
||||
use app\adminapi\validate\system\MenuValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\scope\global\AccessPermissionScope;
|
||||
use app\service\admin\system\MenuService;
|
||||
use app\service\core\plugin\PluginService;
|
||||
use core\exception\handler\AdminException;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use OpenApi\Attributes\RequestBody;
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class MenuController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(MenuService $service, MenuValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/menu',
|
||||
summary: '列表',
|
||||
tags: ['菜单管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => MenuQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:menu:list')]
|
||||
#[PageResponse(schema: MenuResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/menu/{id}',
|
||||
summary: '详情',
|
||||
tags: ['菜单管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'id',
|
||||
description: '菜单ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: 1)
|
||||
),
|
||||
],
|
||||
)]
|
||||
#[Permission(code: 'system:menu:read')]
|
||||
#[SimpleResponse(schema: MenuResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/system/menu',
|
||||
summary: '创建',
|
||||
tags: ['菜单管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => MenuFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:menu:create')]
|
||||
#[SimpleResponse(schema:[],example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
$this->service->cacheDriver()->delete(MenuService::CACHE_ALL_AUTHS_DATA);
|
||||
$this->service->cacheDriver()->delete(MenuService::CACHE_ALL_MENUS_DATA);
|
||||
return parent::store($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/system/menu/{id}',
|
||||
summary: '更新',
|
||||
tags: ['菜单管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => MenuFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: '菜单ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: 1)
|
||||
)]
|
||||
#[Permission(code: 'system:menu:update')]
|
||||
#[SimpleResponse(schema:[],example: [])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $request->all();
|
||||
$model = $this->service->update($id, $data);
|
||||
return Json::success('更新成功', $model->toArray());
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/system/menu',
|
||||
summary: '删除',
|
||||
tags: ['菜单管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:menu:delete')]
|
||||
#[SimpleResponse(schema:[],example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->getDeleteIds($request);
|
||||
if (empty($data)) {
|
||||
throw new AdminException('参数错误');
|
||||
}
|
||||
$result = $this->service->batchDelete($data);
|
||||
$this->service->cacheDriver()->delete(MenuService::CACHE_ALL_AUTHS_DATA);
|
||||
$this->service->cacheDriver()->delete(MenuService::CACHE_ALL_MENUS_DATA);
|
||||
return Json::success('ok', $result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/system/menu/batch-store',
|
||||
summary: '批量添加菜单',
|
||||
tags: ['菜单管理'],
|
||||
)]
|
||||
#[Permission(code: 'system:menu:batch_store')]
|
||||
#[RequestBody(required: true, content: new OA\JsonContent(type: 'array', items: new OA\Items(ref: MenuFormRequest::class)))]
|
||||
#[SimpleResponse(schema:[],example: [])]
|
||||
public function batchStore(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$params = $request->input('menus', []);
|
||||
$data = [];
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
foreach ($params as $param) {
|
||||
$data[] = $this->inputFilter($param);
|
||||
if (!$this->validate->scene('batch-store')->check($param)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($data as $item) {
|
||||
$this->service->save($item);
|
||||
}
|
||||
$this->service->cacheDriver()->delete(MenuService::CACHE_ALL_AUTHS_DATA);
|
||||
$this->service->cacheDriver()->delete(MenuService::CACHE_ALL_MENUS_DATA);
|
||||
return Json::success('ok');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/menu/app/list',
|
||||
summary: '获取应用列表',
|
||||
tags: ['菜单管理'],
|
||||
)]
|
||||
#[Permission(code: 'system:menu:app_list')]
|
||||
#[SimpleResponse(schema:[],example: [])]
|
||||
public function appList(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
/**@var PluginService $service */
|
||||
$service = Container::make(PluginService::class);
|
||||
$model = $service->selectList([], '*', 0, 0, '', [], false, [AccessPermissionScope::class]);
|
||||
return $this->formatAppSelect($model);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail('获取应用列表失败', []);
|
||||
}
|
||||
}
|
||||
|
||||
private function formatAppSelect($items): \support\Response
|
||||
{
|
||||
$formatted_items = [];
|
||||
$formatted_items[] = ['label' => '系统应用', 'value' => 'admin'];
|
||||
foreach ($items as $item) {
|
||||
$formatted_items[] = [
|
||||
'label' => $item->title ?? $item->name ?? $item->real_name ?? $item->id,
|
||||
'value' => $item->id,
|
||||
];
|
||||
}
|
||||
return Json::success('ok', $formatted_items);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\system;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\system\RecycleBinQueryRequest;
|
||||
use app\adminapi\schema\response\system\RecycleBinResponse;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\system\RecycleBinService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class RecycleBinController extends Crud
|
||||
{
|
||||
public function __construct(RecycleBinService $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/recycle',
|
||||
summary: '列表',
|
||||
tags: ['数据回收站'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => RecycleBinQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:recycle:list')]
|
||||
#[PageResponse(schema: RecycleBinResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/recycle/{id}',
|
||||
summary: '详情',
|
||||
tags: ['数据回收站'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:recycle:read')]
|
||||
#[SimpleResponse(schema: RecycleBinResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/system/recycle/{id}',
|
||||
summary: '删除',
|
||||
tags: ['数据回收站'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:recycle:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/system/recycle',
|
||||
summary: '批量删除',
|
||||
tags: ['数据回收站'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:recycle:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/system/recycle/{id}/restore',
|
||||
summary: '恢复',
|
||||
tags: ['数据回收站'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:recycle:recover')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function restore(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->input('id');
|
||||
$this->service->restoreRecycleBin($id);
|
||||
return Json::success('ok');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\system;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\system\RoleFormRequest;
|
||||
use app\adminapi\schema\request\system\RoleQueryRequest;
|
||||
use app\adminapi\schema\response\system\RoleResponse;
|
||||
use app\adminapi\validate\system\RoleValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\scope\global\AccessPermissionScope;
|
||||
use app\service\admin\org\DeptService;
|
||||
use app\service\admin\system\RoleService;
|
||||
use core\exception\handler\AdminException;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use OpenApi\Attributes\RequestBody;
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use support\annotation\Middleware;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class RoleController extends Crud
|
||||
{
|
||||
public function __construct(RoleService $service, RoleValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/role',
|
||||
summary: '列表',
|
||||
tags: ['角色管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => RoleQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: ['system:role:list'])]
|
||||
#[PageResponse(schema: RoleResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/role/{id}',
|
||||
summary: '详情',
|
||||
tags: ['角色管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:role:read')]
|
||||
#[SimpleResponse(schema: RoleResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $this->service->get($id, ['*'], ['scopes']);
|
||||
if (empty($data)) {
|
||||
throw new AdminException('数据未找到', ['errorCode' => 400]);
|
||||
}
|
||||
return Json::success('ok', $data->toArray());
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/system/role',
|
||||
summary: '新增',
|
||||
tags: ['角色管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => RoleFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:role:create')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$data = $this->inputFilter($request->all(), ['permissions']);
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
if (!$this->validate->scene('store')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
$model = $this->service->save($data);
|
||||
if (empty($model)) {
|
||||
throw new AdminException('插入失败');
|
||||
}
|
||||
$pk = $model->getPk();
|
||||
return Json::success('ok', [$pk => $model->getData($pk)]);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/system/role/{id}',
|
||||
summary: '更新',
|
||||
tags: ['角色管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => RoleFormRequest::class,
|
||||
]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: 'ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: 1)
|
||||
)]
|
||||
#[Permission(code: 'system:role:update')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $this->inputFilter($request->all(), ['permissions', 'scopes']);
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
if (!$this->validate->scene('update')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
$this->service->update($id, $data);
|
||||
return Json::success('ok', []);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/system/role/{id}',
|
||||
summary: '删除',
|
||||
tags: ['角色管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:role:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/system/role',
|
||||
summary: '批量删除',
|
||||
tags: ['角色管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'system:role:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/system/role/{id}/data-scope',
|
||||
summary: '分配数据权限',
|
||||
tags: ['角色管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[RequestBody(required: true, content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(property: 'id', type: 'integer', example: 1),
|
||||
new OA\Property(property: 'data_scope', type: 'integer', example: 1),
|
||||
new OA\Property(property: 'scopes', type: 'array', items: new OA\Items(type: 'string')),
|
||||
]
|
||||
))]
|
||||
#[Permission(code: 'system:role:data_scope')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function dataScope(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $this->inputFilter($request->all(), ['scopes']);
|
||||
if (isset($this->validate) && $this->validate) {
|
||||
if (!$this->validate->scene('data-scope')->check($data)) {
|
||||
throw new \Exception($this->validate->getError());
|
||||
}
|
||||
}
|
||||
$this->service->updateScope($id, $data);
|
||||
return Json::success('ok', []);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/system/role/{id}/change-status',
|
||||
summary: '切换状态',
|
||||
tags: ['角色管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[RequestBody(required: true, content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(property: 'status', type: 'string', example: 1),
|
||||
]
|
||||
))]
|
||||
#[Permission(code: 'system:role:status')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function changeStatus(Request $request): \support\Response
|
||||
{
|
||||
return parent::changeStatus($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/role/scope/dept',
|
||||
summary: '获取部门权限树',
|
||||
tags: ['角色管理']
|
||||
)]
|
||||
public function getDeptScope(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
/** @var DeptService $service */
|
||||
$service = Container::make(DeptService::class);
|
||||
$iterm = $service->selectList([], ['id', 'pid', 'name'], 0, 0, 'created_at', [], false, [AccessPermissionScope::class]);
|
||||
return $this->formatTree($iterm);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\system;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\service\admin\system\RuleService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class RuleController extends Crud
|
||||
{
|
||||
public function __construct(RuleService $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/rule-cate',
|
||||
summary: '获取接口分类(基于tags)',
|
||||
tags: ['接口管理']
|
||||
)]
|
||||
#[Permission(code: 'system:rule:cate')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function cate(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$result = $this->service->getCategories();
|
||||
return Json::success('ok', $result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/system/rule',
|
||||
summary: '获取接口列表(支持分类和搜索)',
|
||||
tags: ['接口管理']
|
||||
)]
|
||||
#[Permission(code: 'system:rule:list')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function list(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$cateId = $request->input('cate_id');
|
||||
$keyword = $request->input('keyword');
|
||||
$result = $this->service->getRoutesByCategory($cateId, $keyword);
|
||||
return Json::success('ok', $result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/system/rule/refresh',
|
||||
summary: '刷新接口扫描缓存',
|
||||
tags: ['接口管理']
|
||||
)]
|
||||
#[Permission(code: 'system:rule:refresh')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function refresh(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$this->service->refresh();
|
||||
return Json::success('刷新成功', []);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\controller\system;
|
||||
|
||||
use app\adminapi\controller\Base;
|
||||
use app\attribute\SkipApiEncrypt;
|
||||
use core\tool\ApiEncryptService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\DataResponse;
|
||||
use madong\swagger\attribute\AllowAnonymous;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
#[SkipApiEncrypt]
|
||||
final class SecurityController extends Base
|
||||
{
|
||||
protected ApiEncryptService $encryptService;
|
||||
|
||||
public function __construct(ApiEncryptService $encryptService)
|
||||
{
|
||||
$this->encryptService = $encryptService;
|
||||
}
|
||||
|
||||
#[OA\Get(path: '/system/security/api-encrypt/public-key', summary: '获取接口加密公钥', tags: ['system-security'])]
|
||||
#[DataResponse(description: '获取成功')]
|
||||
#[AllowAnonymous(requireToken: false, requirePermission: false, description: '公共接口')]
|
||||
public function publicKey(Request $request): Response
|
||||
{
|
||||
$publicKey = $this->encryptService->getPublicKey();
|
||||
if ($publicKey === '') {
|
||||
return Json::fail('接口加密公钥未配置');
|
||||
}
|
||||
|
||||
return Json::success('获取成功', [
|
||||
'enabled' => $this->encryptService->isEnabled(),
|
||||
'scope' => $this->encryptService->getScope(),
|
||||
'algorithm' => $this->encryptService->getAlgorithm(),
|
||||
'key_version' => $this->encryptService->getKeyVersion(),
|
||||
'public_key' => $publicKey,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,400 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\terminal;
|
||||
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\service\core\terminal\Terminal;
|
||||
use core\tool\Json;
|
||||
use core\tool\Sse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\AllowAnonymous;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use support\annotation\Middleware;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class TerminalController
|
||||
{
|
||||
|
||||
#[OA\Get(
|
||||
path: "/terminal/config",
|
||||
summary: "获取终端配置",
|
||||
tags: ["终端管理"]
|
||||
)]
|
||||
#[Permission(code: "terminal:config:read")]
|
||||
#[AllowAnonymous(requireToken: false, requirePermission: false)]
|
||||
#[SimpleResponse(schema: [], example: [
|
||||
'enabled' => true,
|
||||
'npm_package_manager' => 'pnpm',
|
||||
'npm_registry' => 'npm',
|
||||
'composer_registry' => 'composer',
|
||||
'package_managers' => [
|
||||
'npm' => [
|
||||
'name' => 'NPM',
|
||||
'install' => 'npm install',
|
||||
'build' => 'npm run build',
|
||||
'check' => 'npm --version',
|
||||
],
|
||||
'cnpm' => [
|
||||
'name' => 'CNPM',
|
||||
'install' => 'cnpm install',
|
||||
'build' => 'cnpm run build',
|
||||
'check' => 'cnpm --version',
|
||||
],
|
||||
'pnpm' => [
|
||||
'name' => 'PNPM',
|
||||
'install' => 'pnpm install',
|
||||
'build' => 'pnpm run build',
|
||||
'check' => 'pnpm --version',
|
||||
],
|
||||
'yarn' => [
|
||||
'name' => 'YARN',
|
||||
'install' => 'yarn install',
|
||||
'build' => 'yarn build',
|
||||
'check' => 'yarn --version',
|
||||
],
|
||||
],
|
||||
'frontend_programs' => [
|
||||
'admin' => [
|
||||
'enabled' => true,
|
||||
'source_dir' => '{project_root}/admin/dist',
|
||||
'target_dir' => '{backend_root}/public/admin',
|
||||
'copy_mappings' => [
|
||||
'*' => '.',
|
||||
],
|
||||
'clean_target' => true,
|
||||
'preserve_files' => [
|
||||
'.gitkeep',
|
||||
],
|
||||
'copy_options' => [
|
||||
'recursive' => true,
|
||||
'overwrite' => true,
|
||||
'preserve_permissions' => true,
|
||||
],
|
||||
],
|
||||
'web' => [
|
||||
'enabled' => true,
|
||||
'source_dir' => '{project_root}/web/.output/public',
|
||||
'target_dir' => '{backend_root}/public',
|
||||
'copy_mappings' => [
|
||||
'*' => '.',
|
||||
],
|
||||
'clean_target' => true,
|
||||
'preserve_files' => [
|
||||
'.gitkeep',
|
||||
],
|
||||
'copy_options' => [
|
||||
'recursive' => true,
|
||||
'overwrite' => true,
|
||||
'preserve_permissions' => true,
|
||||
],
|
||||
],
|
||||
'h5' => [
|
||||
'enabled' => true,
|
||||
'source_dir' => '{project_root}/uni-app/dist/build/h5',
|
||||
'target_dir' => '{backend_root}/public/h5',
|
||||
'copy_mappings' => [
|
||||
'*' => '.',
|
||||
],
|
||||
'clean_target' => true,
|
||||
'preserve_files' => [
|
||||
'.gitkeep',
|
||||
],
|
||||
'copy_options' => [
|
||||
'recursive' => true,
|
||||
'overwrite' => true,
|
||||
'preserve_permissions' => true,
|
||||
],
|
||||
],
|
||||
'app' => [
|
||||
'enabled' => true,
|
||||
'source_dir' => '{project_root}/uni-app/dist/build/app',
|
||||
'target_dir' => '{backend_root}/public/app',
|
||||
'copy_mappings' => [
|
||||
'*' => '.',
|
||||
],
|
||||
'clean_target' => true,
|
||||
'preserve_files' => [
|
||||
'.gitkeep',
|
||||
],
|
||||
'copy_options' => [
|
||||
'recursive' => true,
|
||||
'overwrite' => true,
|
||||
'preserve_permissions' => true,
|
||||
],
|
||||
],
|
||||
'uni-app' => [
|
||||
'enabled' => true,
|
||||
'platforms' => [
|
||||
'h5' => [
|
||||
'source_dir' => '{project_root}/uni-app/dist/build/h5',
|
||||
'target_dir' => '{backend_root}/public/uni-app/h5',
|
||||
'copy_mappings' => [
|
||||
'*' => '.',
|
||||
],
|
||||
],
|
||||
'mp-weixin' => [
|
||||
'source_dir' => '{project_root}/uni-app/dist/build/mp-weixin',
|
||||
'target_dir' => '{backend_root}/public/uni-app/mp-weixin',
|
||||
'copy_mappings' => [
|
||||
'*' => '.',
|
||||
],
|
||||
],
|
||||
'app-plus' => [
|
||||
'source_dir' => '{project_root}/uni-app/dist/build/app-plus',
|
||||
'target_dir' => '{backend_root}/public/uni-app/app-plus',
|
||||
'copy_mappings' => [
|
||||
'*' => '.',
|
||||
],
|
||||
],
|
||||
],
|
||||
'default_platform' => 'h5',
|
||||
'clean_target' => true,
|
||||
'preserve_files' => [
|
||||
'.gitkeep',
|
||||
],
|
||||
'copy_options' => [
|
||||
'recursive' => true,
|
||||
'overwrite' => true,
|
||||
'preserve_permissions' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
])]
|
||||
public function config(Request $request): Response
|
||||
{
|
||||
try {
|
||||
$config = [
|
||||
'enabled' => config('terminal.enabled', false),
|
||||
'npm_package_manager' => config('terminal.npm_package_manager', 'npm'),
|
||||
'npm_registry' => config('terminal.registries.npm.current', 'npm'),
|
||||
'composer_registry' => config('terminal.registries.composer.current', 'composer'),
|
||||
'package_managers' => config('terminal.package_managers', []),
|
||||
'frontend_programs' => config('terminal.frontend_programs', []),
|
||||
];
|
||||
|
||||
return Json::success('获取配置成功', $config);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: "/terminal/config",
|
||||
summary: "更新终端配置",
|
||||
tags: ["终端管理"]
|
||||
)]
|
||||
#[Permission(code: "terminal:config:create")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function updateConfig(Request $request): Response
|
||||
{
|
||||
try {
|
||||
$data = $request->post();
|
||||
|
||||
// 读取当前配置
|
||||
$configFile = base_path() . '/config/terminal.php';
|
||||
$config = require $configFile;
|
||||
|
||||
// 更新包管理器配置
|
||||
if (isset($data['npm_package_manager'])) {
|
||||
$config['npm_package_manager'] = $data['npm_package_manager'];
|
||||
}
|
||||
|
||||
// 保存配置到文件
|
||||
$content = "<?php\n\nreturn " . $this->arrayToPhpConfig($config) . ";\n";
|
||||
file_put_contents($configFile, $content);
|
||||
|
||||
return Json::success('配置更新成功');
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: "/terminal/commands",
|
||||
summary: "获取命令列表(带分组)",
|
||||
tags: ["终端管理"]
|
||||
)]
|
||||
#[Permission(code: "terminal:config:commands")]
|
||||
#[AllowAnonymous(requireToken: false, requirePermission: false)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function commands(Request $request): Response
|
||||
{
|
||||
try {
|
||||
$webConfig = config('terminal.web.command_groups', []);
|
||||
return Json::success('获取命令列表成功', $webConfig);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: "/terminal",
|
||||
summary: "执行命令(SSE方式)",
|
||||
tags: ["终端管理"]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "command",
|
||||
description: "命令key",
|
||||
in: "query",
|
||||
required: true,
|
||||
schema: new OA\Schema(type: "string"),
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "uuid",
|
||||
description: "会话UUID",
|
||||
in: "query",
|
||||
required: true,
|
||||
schema: new OA\Schema(type: "string"),
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "extend",
|
||||
description: "扩展信息",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: new OA\Schema(type: "string"),
|
||||
)]
|
||||
#[Permission(code: "terminal:exec")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function exec(Request $request): void
|
||||
{
|
||||
$connection = $request->connection;
|
||||
$command = $request->input('command');
|
||||
$uuid = $request->input('uuid', '');
|
||||
$extend = $request->input('extend', '');
|
||||
|
||||
// 发送SSE响应头
|
||||
$connection->send(new Response(200, [
|
||||
'Content-Type' => 'text/event-stream',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Connection' => 'keep-alive',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Expose-Headers' => 'Content-Type',
|
||||
'X-Accel-Buffering' => 'no',
|
||||
], "\r\n"));
|
||||
|
||||
// 验证参数
|
||||
if (empty($command) || empty($uuid)) {
|
||||
$connection->send(Sse::error('参数错误:缺少command或uuid参数', null, $uuid));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$terminal = Terminal::create($uuid, $extend);
|
||||
|
||||
foreach ($terminal->exec($command) as $sseMessage) {
|
||||
$connection->send($sseMessage);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$connection->send(Sse::error('执行失败:' . $e->getMessage(), null, $uuid));
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: "/terminal/execute",
|
||||
summary: "执行命令(简单方式)",
|
||||
tags: ["终端管理"]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "command_key",
|
||||
description: "命令key",
|
||||
in: "query",
|
||||
required: true,
|
||||
schema: new OA\Schema(type: "string"),
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "variables",
|
||||
description: "命令变量",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: new OA\Schema(type: "object"),
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: "session_uuid",
|
||||
description: "会话UUID",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: new OA\Schema(type: "string"),
|
||||
)]
|
||||
#[Permission(code: "terminal:execute")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function execute(Request $request): Response
|
||||
{
|
||||
try {
|
||||
$commandKey = $request->post('command_key');
|
||||
$variables = $request->post('variables', []);
|
||||
$sessionUuid = $request->post('session_uuid', '');
|
||||
|
||||
if (!$commandKey) {
|
||||
return Json::fail('命令不能为空');
|
||||
}
|
||||
|
||||
$terminal = Terminal::create($sessionUuid, '');
|
||||
$result = $terminal->executeSimple($commandKey, $variables, $sessionUuid);
|
||||
|
||||
return Json::success($result['message'], $result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数组转换为PHP配置字符串
|
||||
*
|
||||
* @param array $array
|
||||
* @param int $indent
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function arrayToPhpConfig(array $array, int $indent = 0): string
|
||||
{
|
||||
$spaces = str_repeat(' ', $indent);
|
||||
$result = "[\n";
|
||||
|
||||
foreach ($array as $key => $value) {
|
||||
$result .= $spaces . " ";
|
||||
|
||||
// 处理键名
|
||||
if (is_string($key)) {
|
||||
$result .= "'" . str_replace("'", "\\'", $key) . "' => ";
|
||||
} else {
|
||||
$result .= $key . " => ";
|
||||
}
|
||||
|
||||
// 处理值
|
||||
if (is_array($value)) {
|
||||
$result .= $this->arrayToPhpConfig($value, $indent + 1);
|
||||
} elseif (is_string($value)) {
|
||||
$result .= "'" . str_replace("'", "\\'", $value) . "'";
|
||||
} elseif (is_bool($value)) {
|
||||
$result .= $value ? 'true' : 'false';
|
||||
} elseif (is_null($value)) {
|
||||
$result .= 'null';
|
||||
} else {
|
||||
$result .= $value;
|
||||
}
|
||||
|
||||
$result .= ",\n";
|
||||
}
|
||||
|
||||
$result .= $spaces . "]";
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,502 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\upload;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\system\UploadQueryRequest;
|
||||
use app\adminapi\schema\response\system\UploadResponse;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\system\UploadService;
|
||||
use core\exception\handler\AdminException;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use OpenApi\Attributes\RequestBody;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
use Webman\RedisQueue\Client;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class FilesController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(UploadService $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/files',
|
||||
summary: '列表',
|
||||
tags: ['附件管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => UploadQueryRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'upload:files:list')]
|
||||
#[PageResponse(schema: UploadResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/files/{id}',
|
||||
summary: '详情',
|
||||
tags: ['附件管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'upload:files:read')]
|
||||
#[SimpleResponse(schema: UploadResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/files/{id}',
|
||||
summary: '删除',
|
||||
tags: ['附件管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'upload:files:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/files',
|
||||
summary: '批量删除',
|
||||
tags: ['附件管理'],
|
||||
x: [
|
||||
SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'upload:files:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/files/fetch-and-save-image',
|
||||
summary: '上传网络图片到服务器',
|
||||
tags: ['附件管理'],
|
||||
)]
|
||||
#[RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(
|
||||
property: 'url',
|
||||
description: '图片URL',
|
||||
type: 'string',
|
||||
example: 'https://example.com/image.jpg'
|
||||
),
|
||||
]
|
||||
)
|
||||
)]
|
||||
#[Permission(code: 'upload:files:fetch_and_save_image')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function downloadNetworkImage(Request $request): \support\Response
|
||||
{
|
||||
$url = $request->input('url', '');
|
||||
$result = $this->service->saveNetworkImage($url);
|
||||
return Json::success('操作成功', $result);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/files/download-by-id/{id}',
|
||||
summary: '根据ID下载资源',
|
||||
tags: ['附件管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'id',
|
||||
description: '文件ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: '123456789012345678')
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'upload:files:download_by_id')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function downloadResourceById(Request $request): \support\Response|\Webman\Http\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$data = $this->service->get($id);
|
||||
if (empty($data)) {
|
||||
throw new AdminException('数据未找到', ['errorCode' => -1]);
|
||||
}
|
||||
return response()->download($data->path, $data->filename);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/files/download-by-hash/{hash}',
|
||||
summary: '根据hash下载资源',
|
||||
tags: ['附件管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'hash',
|
||||
description: '文件Hash',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: '123456789012345678')
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'upload:files:download_by_hash')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function downloadResourceByHash(Request $request): \support\Response|\Webman\Http\Response
|
||||
{
|
||||
try {
|
||||
$hash = $request->route->param('hash');
|
||||
$data = $this->service->get(['hash' => $hash]);
|
||||
if (empty($data)) {
|
||||
throw new AdminException('数据未找到', ['errorCode' => -1]);
|
||||
}
|
||||
return response()->download($data->path, $data->filename);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), [], $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Throwable
|
||||
*/
|
||||
#[OA\Post(
|
||||
path: '/files/upload-image',
|
||||
summary: '上传图片',
|
||||
tags: ['附件管理'],
|
||||
)]
|
||||
#[Permission(code: 'upload:files:upload_image')]
|
||||
#[RequestBody(
|
||||
required: true,
|
||||
content: new OA\MediaType(
|
||||
mediaType: 'multipart/form-data',
|
||||
schema: new OA\Schema(
|
||||
properties: [
|
||||
new OA\Property(
|
||||
property: 'file',
|
||||
description: '文件',
|
||||
type: 'string',
|
||||
format: 'binary'
|
||||
),
|
||||
new OA\Property(
|
||||
property: 'sub_dir',
|
||||
description: '子目录路径,例如:image/202603',
|
||||
type: 'string',
|
||||
example: 'image/202603'
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
)]
|
||||
#[SimpleResponse(schema: UploadResponse::class, example: [])]
|
||||
public function uploadImage(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$subDir = $request->input('sub_dir', 'image');
|
||||
$subDir .= '/' . date('Ym');
|
||||
$result = $this->service->upload($subDir);
|
||||
return Json::success('ok', $result->toArray());
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/files/upload-file',
|
||||
summary: '上传文件',
|
||||
tags: ['附件管理'],
|
||||
)]
|
||||
#[Permission(code: 'upload:files:upload_file')]
|
||||
#[RequestBody(
|
||||
required: true,
|
||||
content: new OA\MediaType(
|
||||
mediaType: 'multipart/form-data',
|
||||
schema: new OA\Schema(
|
||||
properties: [
|
||||
new OA\Property(
|
||||
property: 'file',
|
||||
description: '文件',
|
||||
type: 'string',
|
||||
format: 'binary'
|
||||
),
|
||||
new OA\Property(
|
||||
property: 'sub_dir',
|
||||
description: '子目录路径,例如:file/202603',
|
||||
type: 'string',
|
||||
example: 'file/202603'
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
)]
|
||||
#[SimpleResponse(schema: UploadResponse::class, example: [])]
|
||||
public function uploadFile(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$subDir = $request->input('sub_dir', 'file');
|
||||
$subDir .= '/' . date('Ym');
|
||||
$result = $this->service->upload($subDir);
|
||||
return Json::success('ok', $result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/files/svg-icons',
|
||||
summary: 'SVG图标列表',
|
||||
tags: ['附件管理'],
|
||||
)]
|
||||
#[Permission(code: 'upload:files:upload_file')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function svgIcons(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$targetDir = public_path() . '/upload/svg-icons';
|
||||
if (!is_dir($targetDir)) {
|
||||
return Json::success('ok', []);
|
||||
}
|
||||
|
||||
$icons = [];
|
||||
foreach (glob($targetDir . '/*.svg') ?: [] as $file) {
|
||||
$name = pathinfo($file, PATHINFO_FILENAME);
|
||||
$content = file_get_contents($file);
|
||||
if ($content === false) {
|
||||
continue;
|
||||
}
|
||||
$icons[] = [
|
||||
'icon' => 'svg:' . $name,
|
||||
'name' => $name,
|
||||
'filename' => basename($file),
|
||||
'url' => '/upload/svg-icons/' . basename($file),
|
||||
'content' => $content,
|
||||
];
|
||||
}
|
||||
|
||||
return Json::success('ok', $icons);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/files/upload-svg-icon',
|
||||
summary: '上传SVG图标',
|
||||
tags: ['附件管理'],
|
||||
)]
|
||||
#[Permission(code: 'upload:files:upload_file')]
|
||||
#[RequestBody(
|
||||
required: true,
|
||||
content: new OA\MediaType(
|
||||
mediaType: 'multipart/form-data',
|
||||
schema: new OA\Schema(
|
||||
properties: [
|
||||
new OA\Property(
|
||||
property: 'file',
|
||||
description: 'SVG图标文件',
|
||||
type: 'string',
|
||||
format: 'binary'
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function uploadSvgIcon(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$file = $request->file('file');
|
||||
if (!$file) {
|
||||
return Json::fail('请选择SVG文件');
|
||||
}
|
||||
if ($file->getSize() > 512 * 1024) {
|
||||
return Json::fail('SVG文件大小不能超过512KB');
|
||||
}
|
||||
|
||||
$extension = strtolower($file->getUploadExtension());
|
||||
$mime = $file->getUploadMimeType();
|
||||
$allowedMimeTypes = ['image/svg+xml', 'image/svg', 'text/xml', 'application/xml', 'text/plain', ''];
|
||||
if ($extension !== 'svg' || !in_array($mime, $allowedMimeTypes, true)) {
|
||||
return Json::fail('只支持SVG格式文件');
|
||||
}
|
||||
|
||||
$content = file_get_contents($file->getPathname());
|
||||
if ($content === false || trim($content) === '') {
|
||||
return Json::fail('SVG文件读取失败');
|
||||
}
|
||||
if (!preg_match('/<svg[\s>]/i', $content)) {
|
||||
return Json::fail('请选择有效的SVG文件');
|
||||
}
|
||||
if (preg_match('/<script|<foreignObject|on\w+\s*=|javascript:/i', $content)) {
|
||||
return Json::fail('SVG文件包含不安全内容');
|
||||
}
|
||||
|
||||
$originalName = pathinfo($file->getUploadName(), PATHINFO_FILENAME);
|
||||
$iconName = preg_replace('/[^a-zA-Z0-9_-]/', '-', $originalName) ?: 'icon';
|
||||
$iconName = trim($iconName, '-_') ?: 'icon';
|
||||
$targetDir = public_path() . '/upload/svg-icons';
|
||||
if (!is_dir($targetDir) && !mkdir($targetDir, 0755, true) && !is_dir($targetDir)) {
|
||||
return Json::fail('SVG图标目录创建失败');
|
||||
}
|
||||
|
||||
$targetPath = $targetDir . '/' . $iconName . '.svg';
|
||||
if (is_file($targetPath)) {
|
||||
$iconName .= '-' . date('YmdHis');
|
||||
$targetPath = $targetDir . '/' . $iconName . '.svg';
|
||||
}
|
||||
if (file_put_contents($targetPath, $content, LOCK_EX) === false) {
|
||||
return Json::fail('SVG图标保存失败');
|
||||
}
|
||||
|
||||
return Json::success('上传成功', [
|
||||
'icon' => 'svg:' . $iconName,
|
||||
'name' => $iconName,
|
||||
'filename' => $iconName . '.svg',
|
||||
'url' => '/upload/svg-icons/' . $iconName . '.svg',
|
||||
'path' => str_replace('\\', '/', $targetPath),
|
||||
'content' => $content,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传图片返回base64
|
||||
* 用于插件开发等需要base64预览的场景
|
||||
*/
|
||||
#[OA\Post(
|
||||
path: '/files/upload-image-base64',
|
||||
summary: '上传图片返回base64',
|
||||
tags: ['附件管理'],
|
||||
)]
|
||||
#[Permission(code: 'upload:files:upload_image_base64')]
|
||||
#[RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(
|
||||
property: 'file',
|
||||
description: '图片文件',
|
||||
type: 'string',
|
||||
format: 'binary'
|
||||
),
|
||||
]
|
||||
)
|
||||
)]
|
||||
public function uploadImageBase64(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$file = $request->file('file');
|
||||
if (!$file) {
|
||||
return Json::fail('请选择图片文件');
|
||||
}
|
||||
|
||||
// 验证文件类型
|
||||
$allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'];
|
||||
if (!in_array($file->getUploadMimeType(), $allowedTypes)) {
|
||||
return Json::fail('只支持PNG、JPEG、JPG、GIF格式的图片');
|
||||
}
|
||||
|
||||
// 验证文件大小(最大2MB)
|
||||
$maxSize = 2 * 1024 * 1024;
|
||||
if ($file->getSize() > $maxSize) {
|
||||
return Json::fail('图片大小不能超过2MB');
|
||||
}
|
||||
|
||||
// 读取文件内容并转换为base64
|
||||
$imageData = file_get_contents($file->getPathname());
|
||||
if ($imageData === false) {
|
||||
return Json::fail('图片读取失败');
|
||||
}
|
||||
|
||||
$base64 = 'data:' . $file->getUploadMimeType() . ';base64,' . base64_encode($imageData);
|
||||
|
||||
return Json::success('上传成功', [
|
||||
'base64' => $base64,
|
||||
'size' => $file->getSize(),
|
||||
'mime' => $file->getUploadMimeType(),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/export/download-excel',
|
||||
summary: '下载导出的excel',//下载导出的excel-后期需要优化实现下载成功后删除原文件
|
||||
tags: ['附件管理'],
|
||||
)]
|
||||
#[Permission(code: 'upload:files:download_excel')]
|
||||
public function downloadExcel(Request $request): ?\support\Response
|
||||
{
|
||||
|
||||
$param = $request->all();
|
||||
$file = $param['file_path'];//文件路径
|
||||
$downloadName = $param['file'] ?? date('Y-m-d His', time());//文件下载名称
|
||||
$filePath = runtime_path() . $file;
|
||||
//生成的文件5分钟后超时自动删除
|
||||
$queue = 'remove-excel-file';
|
||||
Client::send($queue, $param, 300);
|
||||
return response()->download($filePath, $downloadName);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/files/common/wangeditor',
|
||||
summary: '适配wangeditor编辑器上传接口',
|
||||
tags: ['附件管理'],
|
||||
)]
|
||||
#[Permission(code: 'upload:files:wangeditor')]
|
||||
public function wangeditor(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$subDir = $request->input('sub_dir', 'image');
|
||||
$subDir .= '/' . date('Ym');
|
||||
$result = $this->service->upload($subDir);
|
||||
$data = $result->toArray();
|
||||
$data['url'] = $data['base_path'] ?? '';
|
||||
return Json(['errno' => 0, 'data' => $data]);
|
||||
} catch (\Exception $e) {
|
||||
return Json([
|
||||
'errno' => 1,
|
||||
'message' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\web;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\validate\web\LinkValidate;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\web\LinkService;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class LinkController extends Crud
|
||||
{
|
||||
public function __construct(LinkService $service, LinkValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/web/link',
|
||||
summary: '友情链接列表',
|
||||
tags: ['友情链接管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "name", description: "链接名称", in: "query", schema: new OA\Schema(type: "string")),
|
||||
new OA\Parameter(name: "status", description: "状态", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "page", description: "页码", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "limit", description: "每页数量", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "获取成功"),
|
||||
new OA\Property(property: "data", properties: [
|
||||
new OA\Property(property: "total", type: "integer", example: 10),
|
||||
new OA\Property(property: "items", type: "array", items: new OA\Items(properties: [
|
||||
new OA\Property(property: "id", type: "integer", example: 1),
|
||||
new OA\Property(property: "name", type: "string", example: "百度"),
|
||||
new OA\Property(property: "url", type: "string", example: "https://www.baidu.com"),
|
||||
new OA\Property(property: "logo", type: "string", example: ""),
|
||||
new OA\Property(property: "logo_type", type: "integer", example: 2, description: "图标类型:1=上传,2=链接"),
|
||||
new OA\Property(property: "sort", type: "integer", example: 1),
|
||||
new OA\Property(property: "enabled", type: "integer", example: 1),
|
||||
])),
|
||||
], type: "object"),
|
||||
])
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission("web:link:list")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
[$where, $format, $limit, $field, $order, $page] = $this->selectInput($request);
|
||||
$methods = [
|
||||
'select' => 'formatSelect',
|
||||
'tree' => 'formatTree',
|
||||
'table_tree' => 'formatTableTree',
|
||||
'normal' => 'formatNormal',
|
||||
];
|
||||
$format_function = $methods[$format] ?? 'formatNormal';
|
||||
$total = $this->service->getCount($where);
|
||||
$list = $this->service->selectList($where, $field, $page, $limit, $order, [], false);
|
||||
return call_user_func([$this, $format_function], $list, $total);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/web/link/{id}',
|
||||
summary: '友情链接详情',
|
||||
tags: ['友情链接管理'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission("web:link:read")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/web/link',
|
||||
summary: '创建友情链接',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "name", type: "string", example: "百度"),
|
||||
new OA\Property(property: "url", type: "string", example: "https://www.baidu.com"),
|
||||
new OA\Property(property: "logo", type: "string", example: ""),
|
||||
new OA\Property(property: "logo_type", type: "integer", example: 2, description: "图标类型:1=上传,2=链接"),
|
||||
new OA\Property(property: "sort", type: "integer", example: 1),
|
||||
new OA\Property(property: "enabled", type: "integer", example: 1),
|
||||
])
|
||||
),
|
||||
tags: ['友情链接管理'],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "创建成功"),
|
||||
new OA\Property(property: "data", properties: [
|
||||
new OA\Property(property: "id", type: "integer", example: 1),
|
||||
], type: "object"),
|
||||
])
|
||||
),
|
||||
],
|
||||
)]
|
||||
#[Permission("web:link:create")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
return parent::store($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/web/link/{id}',
|
||||
summary: '更新友情链接',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "name", type: "string", example: "百度"),
|
||||
new OA\Property(property: "url", type: "string", example: "https://www.baidu.com"),
|
||||
new OA\Property(property: "logo", type: "string", example: ""),
|
||||
new OA\Property(property: "logo_type", description: "图标类型:1=上传,2=链接", type: "integer", example: 2),
|
||||
new OA\Property(property: "sort", type: "integer", example: 1),
|
||||
new OA\Property(property: "enabled", type: "integer", example: 1),
|
||||
])
|
||||
),
|
||||
tags: ['友情链接管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "链接ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "更新成功"),
|
||||
new OA\Property(property: "data", properties: [
|
||||
new OA\Property(property: "id", type: "integer", example: 1),
|
||||
], type: "object"),
|
||||
])
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission("web:link:update")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
return parent::update($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/web/link/{id}',
|
||||
summary: '删除友情链接',
|
||||
tags: ['友情链接管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "链接ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "删除成功"),
|
||||
])
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission("web:link:delete")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\web;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\validate\web\MenuValidate;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\web\MenuService;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class MenuController extends Crud
|
||||
{
|
||||
public function __construct(MenuService $service, MenuValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/web/menu',
|
||||
summary: '菜单列表',
|
||||
tags: ['菜单管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "name", description: "菜单名称", in: "query", schema: new OA\Schema(type: "string")),
|
||||
new OA\Parameter(name: "type", description: "菜单类型", in: "query", schema: new OA\Schema(type: "string")),
|
||||
new OA\Parameter(name: "page", description: "页码", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
new OA\Parameter(name: "limit", description: "每页数量", in: "query", schema: new OA\Schema(type: "integer")),
|
||||
]
|
||||
)]
|
||||
#[Permission("web:menu:list")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
[$where, $format, $limit, $field, $order, $page] = $this->selectInput($request);
|
||||
$methods = [
|
||||
'select' => 'formatSelect',
|
||||
'tree' => 'formatTree',
|
||||
'table_tree' => 'formatTableTree',
|
||||
'normal' => 'formatNormal',
|
||||
];
|
||||
if (empty($order)) {
|
||||
$order = 'sort asc';
|
||||
}
|
||||
$format_function = $methods[$format] ?? 'formatNormal';
|
||||
$total = $this->service->getCount($where);
|
||||
$list = $this->service->selectList($where, $field, $page, $limit, $order, [], false);
|
||||
return call_user_func([$this, $format_function], $list, $total);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/web/menu/{id}',
|
||||
summary: '菜单详情',
|
||||
tags: ['菜单管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "菜单ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "获取成功"),
|
||||
new OA\Property(property: "data", properties: [
|
||||
new OA\Property(property: "id", type: "integer", example: 1),
|
||||
new OA\Property(property: "name", type: "string", example: "首页"),
|
||||
new OA\Property(property: "type", type: "string", example: "page"),
|
||||
new OA\Property(property: "path", type: "string", example: "/home"),
|
||||
new OA\Property(property: "title", type: "string", example: "首页"),
|
||||
new OA\Property(property: "url", type: "string", example: ""),
|
||||
new OA\Property(property: "icon", type: "string", example: "home"),
|
||||
new OA\Property(property: "sort", type: "integer", example: 1),
|
||||
], type: "object"),
|
||||
])
|
||||
),
|
||||
],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission("web:menu:read")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/web/menu',
|
||||
summary: '创建',
|
||||
tags: ['菜单管理'],
|
||||
)]
|
||||
#[Permission("web:menu:create")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
return parent::store($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/web/menu/{id}',
|
||||
summary: '更新菜单',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "name", type: "string", example: "首页"),
|
||||
new OA\Property(property: "type", type: "string", example: "page"),
|
||||
new OA\Property(property: "path", type: "string", example: "/home"),
|
||||
new OA\Property(property: "title", type: "string", example: "首页"),
|
||||
new OA\Property(property: "url", type: "string", example: ""),
|
||||
new OA\Property(property: "icon", type: "string", example: "home"),
|
||||
new OA\Property(property: "sort", type: "integer", example: 1),
|
||||
])
|
||||
),
|
||||
tags: ['菜单管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "菜单ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "更新成功"),
|
||||
new OA\Property(property: "data", properties: [
|
||||
new OA\Property(property: "id", type: "integer", example: 1),
|
||||
], type: "object"),
|
||||
])
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission("web:menu:update")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
return parent::update($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/web/menu',
|
||||
summary: '删除菜单',
|
||||
tags: ['菜单管理'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: "id", description: "菜单ID", in: "path", required: true, schema: new OA\Schema(type: "integer")),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: "成功",
|
||||
content: new OA\JsonContent(properties: [
|
||||
new OA\Property(property: "code", type: "integer", example: 0),
|
||||
new OA\Property(property: "msg", type: "string", example: "删除成功"),
|
||||
new OA\Property(property: "data", properties: [
|
||||
new OA\Property(property: "ids", type: "array", items: new OA\Items(type: "integer")),
|
||||
]),
|
||||
])
|
||||
),
|
||||
],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission("web:menu:delete")]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$ids = $request->input('ids',[]);
|
||||
$this->service->batchDelete($ids);
|
||||
return Json::success('删除成功');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\event;
|
||||
|
||||
use Webman\Event\Event;
|
||||
|
||||
/**
|
||||
* 登录日志事件
|
||||
*/
|
||||
class LoginLogEvent
|
||||
{
|
||||
/**
|
||||
* 操作名称
|
||||
*/
|
||||
public string $name;
|
||||
|
||||
/**
|
||||
* 应用名称
|
||||
*/
|
||||
public string $app;
|
||||
|
||||
/**
|
||||
* IP地址
|
||||
*/
|
||||
public string $ip;
|
||||
|
||||
/**
|
||||
* IP归属地
|
||||
*/
|
||||
public string $ipLocation;
|
||||
|
||||
/**
|
||||
* 浏览器
|
||||
*/
|
||||
public string $browser;
|
||||
|
||||
/**
|
||||
* 操作系统
|
||||
*/
|
||||
public string $os;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
public int $status;
|
||||
|
||||
/**
|
||||
* 消息
|
||||
*/
|
||||
public string $message;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
public string $userName;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
public string|null|int $userId;
|
||||
|
||||
/**
|
||||
* 登录时间
|
||||
*/
|
||||
public int $loginTime;
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
public string $accessToken;
|
||||
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
public int $expiresAt;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
string $app,
|
||||
string $ip,
|
||||
string $ipLocation,
|
||||
string $browser,
|
||||
string $os,
|
||||
int $status,
|
||||
string $message,
|
||||
string $userName,
|
||||
string|null|int $userId,
|
||||
int $loginTime,
|
||||
string $accessToken,
|
||||
int $expiresAt
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->app = $app;
|
||||
$this->ip = $ip;
|
||||
$this->ipLocation = $ipLocation;
|
||||
$this->browser = $browser;
|
||||
$this->os = $os;
|
||||
$this->status = $status;
|
||||
$this->message = $message;
|
||||
$this->userName = $userName;
|
||||
$this->userId = $userId;
|
||||
$this->loginTime = $loginTime;
|
||||
$this->accessToken = $accessToken;
|
||||
$this->expiresAt = $expiresAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
*/
|
||||
public function dispatch(): void
|
||||
{
|
||||
Event::emit('adminapi.login.log', $this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\event;
|
||||
|
||||
use Webman\Event\Event;
|
||||
|
||||
/**
|
||||
* 会员等级更新事件
|
||||
*
|
||||
* 用于处理会员等级变动的场景:
|
||||
* - 积分增加导致等级提升
|
||||
* - 积分扣减导致等级下降
|
||||
* - 手动调整会员等级
|
||||
*/
|
||||
class MemberLevelUpdatedEvent
|
||||
{
|
||||
/**
|
||||
* 会员ID
|
||||
*/
|
||||
public int|string $memberId;
|
||||
|
||||
/**
|
||||
* 变动前等级ID
|
||||
*/
|
||||
public int|string|null $oldLevelId;
|
||||
|
||||
/**
|
||||
* 变动后等级ID
|
||||
*/
|
||||
public int|string|null $newLevelId;
|
||||
|
||||
/**
|
||||
* 变动前等级名称
|
||||
*/
|
||||
public ?string $oldLevelName;
|
||||
|
||||
/**
|
||||
* 变动后等级名称
|
||||
*/
|
||||
public ?string $newLevelName;
|
||||
|
||||
/**
|
||||
* 触发原因(points_increase=积分增加, points_deducted=积分扣减, admin=后台调整, other=其他)
|
||||
*/
|
||||
public string $reason;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
public string $remark;
|
||||
|
||||
/**
|
||||
* 额外数据(可选,用于存储其他相关信息)
|
||||
*/
|
||||
public ?array $extra;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct(
|
||||
int|string $memberId,
|
||||
int|string|null $oldLevelId,
|
||||
int|string|null $newLevelId,
|
||||
?string $oldLevelName,
|
||||
?string $newLevelName,
|
||||
string $reason,
|
||||
string $remark = '',
|
||||
?array $extra = null
|
||||
) {
|
||||
$this->memberId = $memberId;
|
||||
$this->oldLevelId = $oldLevelId;
|
||||
$this->newLevelId = $newLevelId;
|
||||
$this->oldLevelName = $oldLevelName;
|
||||
$this->newLevelName = $newLevelName;
|
||||
$this->reason = $reason;
|
||||
$this->remark = $remark;
|
||||
$this->extra = $extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
*/
|
||||
public function dispatch(): void
|
||||
{
|
||||
Event::emit('adminapi.member.level.updated', $this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\event;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Webman\Event\Event;
|
||||
|
||||
/**
|
||||
* 菜单格式化事件
|
||||
*/
|
||||
class MenuFormattingEvent
|
||||
{
|
||||
/**
|
||||
* 菜单数据
|
||||
*/
|
||||
public Collection|null $data;
|
||||
|
||||
/**
|
||||
* 格式化类型
|
||||
*/
|
||||
public string $formatType;
|
||||
|
||||
/**
|
||||
* 结果
|
||||
*/
|
||||
public array $result = [];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct(
|
||||
Collection|null $data,
|
||||
string $formatType
|
||||
) {
|
||||
$this->data = $data;
|
||||
$this->formatType = $formatType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
*/
|
||||
public function dispatch(): array
|
||||
{
|
||||
Event::emit('adminapi.menu.formatting', $this);
|
||||
return $this->result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\event;
|
||||
|
||||
use Webman\Event\Event;
|
||||
|
||||
/**
|
||||
* 操作日志事件
|
||||
*/
|
||||
class OperationLogEvent
|
||||
{
|
||||
/**
|
||||
* 操作名称
|
||||
*/
|
||||
public string $name;
|
||||
|
||||
/**
|
||||
* OA 标签
|
||||
*/
|
||||
public array $oaTags;
|
||||
|
||||
/**
|
||||
* OA 描述
|
||||
*/
|
||||
public string $oaDescription;
|
||||
|
||||
/**
|
||||
* 应用名称
|
||||
*/
|
||||
public string $app;
|
||||
|
||||
/**
|
||||
* IP地址
|
||||
*/
|
||||
public string $ip;
|
||||
|
||||
/**
|
||||
* IP归属地
|
||||
*/
|
||||
public string $ipLocation;
|
||||
|
||||
/**
|
||||
* 浏览器
|
||||
*/
|
||||
public string $browser;
|
||||
|
||||
/**
|
||||
* 操作系统
|
||||
*/
|
||||
public string $os;
|
||||
|
||||
/**
|
||||
* 请求URL
|
||||
*/
|
||||
public string $url;
|
||||
|
||||
/**
|
||||
* 控制器类名
|
||||
*/
|
||||
public string $className;
|
||||
|
||||
/**
|
||||
* 操作方法
|
||||
*/
|
||||
public string $action;
|
||||
|
||||
/**
|
||||
* 请求方法
|
||||
*/
|
||||
public string $method;
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
public string $param;
|
||||
|
||||
/**
|
||||
* 响应结果
|
||||
*/
|
||||
public array|string $result;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
public string $userName;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
public int $userId;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
array $oaTags,
|
||||
string $oaDescription,
|
||||
string $app,
|
||||
string $ip,
|
||||
string $ipLocation,
|
||||
string $browser,
|
||||
string $os,
|
||||
string $url,
|
||||
string $className,
|
||||
string $action,
|
||||
string $method,
|
||||
string $param,
|
||||
array|string $result,
|
||||
string $userName,
|
||||
int $userId
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->oaTags = $oaTags;
|
||||
$this->oaDescription = $oaDescription;
|
||||
$this->app = $app;
|
||||
$this->ip = $ip;
|
||||
$this->ipLocation = $ipLocation;
|
||||
$this->browser = $browser;
|
||||
$this->os = $os;
|
||||
$this->url = $url;
|
||||
$this->className = $className;
|
||||
$this->action = $action;
|
||||
$this->method = $method;
|
||||
$this->param = $param;
|
||||
$this->result = $result;
|
||||
$this->userName = $userName;
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
*/
|
||||
public function dispatch(): void
|
||||
{
|
||||
Event::emit('adminapi.operation.log', $this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\event;
|
||||
|
||||
use app\enum\member\PointType;
|
||||
use app\enum\member\PointSource;
|
||||
use Webman\Event\Event;
|
||||
|
||||
/**
|
||||
* 积分变动事件
|
||||
*
|
||||
* 统一的积分变动事件,用于处理积分的增加和减少
|
||||
*/
|
||||
class PointsChangedEvent
|
||||
{
|
||||
/**
|
||||
* 会员ID
|
||||
*/
|
||||
public int|string $memberId;
|
||||
|
||||
/**
|
||||
* 变动类型
|
||||
*/
|
||||
public PointType $type;
|
||||
|
||||
/**
|
||||
* 变动前积分
|
||||
*/
|
||||
public int $oldPoints;
|
||||
|
||||
/**
|
||||
* 变动后积分
|
||||
*/
|
||||
public int $newPoints;
|
||||
|
||||
/**
|
||||
* 变动的积分数量
|
||||
*/
|
||||
public int $points;
|
||||
|
||||
/**
|
||||
* 积分来源
|
||||
*/
|
||||
public PointSource $source;
|
||||
|
||||
/**
|
||||
* 变动原因/备注
|
||||
*/
|
||||
public string $remark;
|
||||
|
||||
/**
|
||||
* 关联ID(如订单ID、活动ID等,可选)
|
||||
*/
|
||||
public int|string|null $relatedId;
|
||||
|
||||
/**
|
||||
* 额外数据(可选,用于存储其他相关信息)
|
||||
*/
|
||||
public ?array $extra;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct(
|
||||
int|string $memberId,
|
||||
int $points,
|
||||
PointSource $source,
|
||||
PointType $type,
|
||||
int $oldPoints,
|
||||
int $newPoints,
|
||||
string $remark = '',
|
||||
int|string|null $relatedId = null,
|
||||
?array $extra = null
|
||||
) {
|
||||
$this->memberId = $memberId;
|
||||
$this->points = $points;
|
||||
$this->source = $source;
|
||||
$this->type = $type;
|
||||
$this->oldPoints = $oldPoints;
|
||||
$this->newPoints = $newPoints;
|
||||
$this->remark = $remark;
|
||||
$this->relatedId = $relatedId;
|
||||
$this->extra = $extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
*/
|
||||
public function dispatch(): void
|
||||
{
|
||||
Event::emit('adminapi.points.changed', $this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\event;
|
||||
|
||||
use app\enum\review\ReviewStatus;
|
||||
use app\model\review\Review;
|
||||
use Webman\Event\Event;
|
||||
|
||||
/**
|
||||
* 审核通过事件
|
||||
*/
|
||||
class ReviewApprovedEvent
|
||||
{
|
||||
/**
|
||||
* 审核记录
|
||||
*/
|
||||
public Review $review;
|
||||
|
||||
/**
|
||||
* 额外数据
|
||||
*/
|
||||
public array $data;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct(Review $review, array $data = [])
|
||||
{
|
||||
$this->review = $review;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
*/
|
||||
public function dispatch(): void
|
||||
{
|
||||
Event::emit('adminapi.review.approved', $this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\event;
|
||||
|
||||
use app\model\review\Review;
|
||||
use Webman\Event\Event;
|
||||
|
||||
/**
|
||||
* 审核创建事件
|
||||
*/
|
||||
class ReviewCreatedEvent
|
||||
{
|
||||
/**
|
||||
* 审核记录
|
||||
*/
|
||||
public Review $review;
|
||||
|
||||
/**
|
||||
* 额外数据
|
||||
*/
|
||||
public array $data;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct(Review $review, array $data = [])
|
||||
{
|
||||
$this->review = $review;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
*/
|
||||
public function dispatch(): void
|
||||
{
|
||||
Event::emit('adminapi.review.created', $this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\event;
|
||||
|
||||
use app\model\review\Review;
|
||||
use Webman\Event\Event;
|
||||
|
||||
/**
|
||||
* 审核拒绝事件
|
||||
*/
|
||||
class ReviewRejectedEvent
|
||||
{
|
||||
/**
|
||||
* 审核记录
|
||||
*/
|
||||
public Review $review;
|
||||
|
||||
/**
|
||||
* 额外数据
|
||||
*/
|
||||
public array $data;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct(Review $review, array $data = [])
|
||||
{
|
||||
$this->review = $review;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
*/
|
||||
public function dispatch(): void
|
||||
{
|
||||
Event::emit('adminapi.review.rejected', $this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\listener;
|
||||
|
||||
use app\adminapi\event\LoginLogEvent;
|
||||
use app\dao\logs\LoginLogDao;
|
||||
use support\Container;
|
||||
|
||||
/**
|
||||
* 登录日志监听器
|
||||
* 处理登录日志事件,将登录记录保存到数据库
|
||||
*/
|
||||
class LoginLogListener
|
||||
{
|
||||
/**
|
||||
* 处理登录日志事件
|
||||
*
|
||||
* @param LoginLogEvent $event
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle(LoginLogEvent $event): void
|
||||
{
|
||||
// 保存登录日志到数据库
|
||||
$this->saveLoginLog($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存登录日志
|
||||
*
|
||||
* @param LoginLogEvent $event
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function saveLoginLog(LoginLogEvent $event): void
|
||||
{
|
||||
/** @var LoginLogDao $dao */
|
||||
$dao = Container::make(LoginLogDao::class);
|
||||
|
||||
$dao->save([
|
||||
'user_id' => $event->userId,
|
||||
'ip' => $event->ip,
|
||||
'ip_location' => $event->ipLocation,
|
||||
'os' => $event->os,
|
||||
'browser' => $event->browser,
|
||||
'status' => $event->status,
|
||||
'message' => $event->message,
|
||||
'login_time' => $event->loginTime,
|
||||
'key' => md5($event->accessToken),
|
||||
'expires_at' => $event->expiresAt,
|
||||
'remark' => $event->status == 1 ? '登录成功' : '登录失败',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\listener;
|
||||
|
||||
use app\adminapi\event\MemberLevelUpdatedEvent;
|
||||
|
||||
/**
|
||||
* 会员等级更新监听器
|
||||
*
|
||||
* 处理会员等级更新的事件:
|
||||
* 1. 发送等级变更通知
|
||||
* 2. 记录等级变更日志
|
||||
*/
|
||||
class MemberLevelUpdatedListener
|
||||
{
|
||||
/**
|
||||
* 处理会员等级更新事件
|
||||
*/
|
||||
public function handle(MemberLevelUpdatedEvent $event): void
|
||||
{
|
||||
// 发送等级变更通知(如果需要)
|
||||
// $this->sendLevelChangeNotification($event);
|
||||
|
||||
// 记录等级变更日志(如果需要)
|
||||
// $this->recordLevelChangeLog($event);
|
||||
|
||||
// 其他等级更新相关的处理
|
||||
$this->handleLevelChange($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理等级变更
|
||||
*/
|
||||
private function handleLevelChange(MemberLevelUpdatedEvent $event): void
|
||||
{
|
||||
// 这里可以添加等级变更后的处理逻辑
|
||||
// 例如:
|
||||
// 1. 给会员发送等级提升的奖励
|
||||
// 2. 更新会员的特权
|
||||
// 3. 记录等级变更的历史
|
||||
|
||||
// 示例:如果是等级提升,可以发送通知
|
||||
if ($event->oldLevelId && $event->newLevelId) {
|
||||
// 等级变更的处理
|
||||
echo "会员 {$event->memberId} 等级从 {$event->oldLevelName} 变更为 {$event->newLevelName}\n";
|
||||
} elseif (!$event->oldLevelId && $event->newLevelId) {
|
||||
// 首次获得等级的处理
|
||||
echo "会员 {$event->memberId} 获得等级 {$event->newLevelName}\n";
|
||||
} elseif ($event->oldLevelId && !$event->newLevelId) {
|
||||
// 失去等级的处理
|
||||
echo "会员 {$event->memberId} 失去等级 {$event->oldLevelName}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\listener;
|
||||
|
||||
use app\adminapi\event\MenuFormattingEvent;
|
||||
use madong\helper\Tree;
|
||||
|
||||
/**
|
||||
* 菜单格式化监听器
|
||||
*/
|
||||
class MenuFormattingListener
|
||||
{
|
||||
/**
|
||||
* 处理菜单格式化事件
|
||||
*
|
||||
* @param MenuFormattingEvent $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(MenuFormattingEvent $event): void
|
||||
{
|
||||
switch ($event->formatType) {
|
||||
case 'vben':
|
||||
$event->result = $this->formatForVben($event->data);
|
||||
break;
|
||||
case 'art':
|
||||
$event->result = $this->formatForArt($event->data);
|
||||
break;
|
||||
case 'tree':
|
||||
case 'default':
|
||||
$event->result = $this->formatForTree($event->data);
|
||||
break;
|
||||
default:
|
||||
$event->result = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础树形结构格式化(用于Vben Ui)
|
||||
*
|
||||
* @param \Illuminate\Support\Collection|null $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function formatForVben(null|\Illuminate\Support\Collection $data): array
|
||||
{
|
||||
if (empty($data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$filteredData = $data->filter(function ($item) {
|
||||
return !in_array($item->type, [3, 4]);
|
||||
})->map(function ($item) {
|
||||
$meta = [
|
||||
// 基础信息
|
||||
'title' => $item->title,
|
||||
'icon' => $item->icon ?? null,
|
||||
'order' => $item->sort ?? 0,
|
||||
|
||||
// 显示控制
|
||||
'hideInMenu' => !$item->is_show ?? false,
|
||||
'hideInTab' => $item->is_hide_tab ?? false,
|
||||
'affixTab' => $item->is_affix ?? false, // 固定标签页
|
||||
|
||||
// 权限控制
|
||||
'authority' => $item->variable ? explode(',', $item->variable) : [],
|
||||
|
||||
// 功能控制
|
||||
'keepAlive' => $item->is_cache ?? false, // 缓存
|
||||
|
||||
// 链接相关
|
||||
'link' => $item->type == 6 ? $item->link_url : null, // 外链(6)使用link_url
|
||||
'iframeSrc' => $item->is_frame ? $item->link_url : null, // iframe链接
|
||||
'openInNewWindow' => $item->open_type === '_blank' ?? false, // 新窗口打开
|
||||
];
|
||||
|
||||
// 3. 徽章配置(如有文本徽章)
|
||||
if (!empty($item->show_text_badge)) {
|
||||
$meta['badge'] = $item->show_text_badge;
|
||||
$meta['badgeType'] = 'normal';
|
||||
$meta['badgeVariants'] = 'default';
|
||||
}
|
||||
// 4. 返回格式化后的菜单项结构
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'pid' => $item->pid,
|
||||
'path' => $item->path,
|
||||
'name' => $item->code,
|
||||
'component' => $item->component,
|
||||
'redirect' => $item->redirect ?? '',
|
||||
'meta' => $meta,
|
||||
];
|
||||
})->toArray();
|
||||
$tree = new Tree($filteredData);
|
||||
return $tree->getTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础树形结构格式化(用于Art Ui)
|
||||
*
|
||||
* @param \Illuminate\Support\Collection|null $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function formatForArt(null|\Illuminate\Support\Collection $data): array
|
||||
{
|
||||
if (empty($data)) {
|
||||
//可以输出默认主页菜单
|
||||
return [];
|
||||
}
|
||||
|
||||
$items = $data->all();
|
||||
$grouped = collect($items)->groupBy('pid')->all();
|
||||
$buildMenu = function ($parentId = 0) use (&$buildMenu, $grouped) {
|
||||
$menuItems = [];
|
||||
$currentItems = $grouped[$parentId] ?? collect();
|
||||
|
||||
|
||||
foreach ($currentItems as $item) {
|
||||
// 跳过按钮(3)和接口(4)类型,它们不直接作为菜单项
|
||||
if (in_array($item->type, [3, 4])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 提取当前菜单项下的接口(4)和按钮(3)作为authList
|
||||
$authItems = $grouped[$item->id] ?? collect();
|
||||
$authList = $authItems->filter(function ($child) {
|
||||
return in_array($child->type, [3, 4]);
|
||||
})->map(function ($authItem) {
|
||||
return [
|
||||
'title' => $authItem->title,//可以优化多语言
|
||||
'authMark' => $authItem->code ?? $authItem->path,
|
||||
];
|
||||
})->all();
|
||||
$meta = [
|
||||
'title' => $item->title,
|
||||
'icon' => $item->icon ?? null,
|
||||
'isHide' => !$item->is_show ?? false, // (0=隐藏,1=显示)
|
||||
'isHideTab' => $item->is_affix ?? false, // 否隐藏标签页
|
||||
'link' => in_array($item->type, [5, 6]) ? $item->link_url : null, //外链使用link_url字段
|
||||
'isIframe' => $item->is_frame ?? false, // 修正:is_frame对应是否为iframe
|
||||
'keepAlive' => $item->is_cache ?? false, // 修正:is_cache对应是否缓存
|
||||
'authList' => $authList,
|
||||
'isFirstLevel' => $parentId === 0, // 顶级菜单标记为一级菜单
|
||||
'roles' => $item->variable ? explode(',', $item->variable) : [], // 假设variable存储角色列表(逗号分隔)
|
||||
];
|
||||
|
||||
// 插件菜单添加 module 字段,值为 app 字段
|
||||
if (($item->source ?? '') === 'plugin' && !empty($item->app)) {
|
||||
$meta['module'] = $item->app;
|
||||
}
|
||||
|
||||
$component = $item->component;
|
||||
$path = $item->path;
|
||||
|
||||
if ($item->type === 1 && $parentId === 0) {
|
||||
$component = '/layout';
|
||||
$path = $path ?: '#'; // 目录默认路径为#
|
||||
}
|
||||
|
||||
$children = $buildMenu($item->id);
|
||||
$menuItems[] = [
|
||||
'id' => $item->id,
|
||||
'path' => $path,
|
||||
'name' => $item->code,
|
||||
'component' => $component,
|
||||
'meta' => $meta,
|
||||
'children' => $children,
|
||||
];
|
||||
}
|
||||
return $menuItems;
|
||||
};
|
||||
return $buildMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础树形结构格式化(用于权限设置等场景)
|
||||
*
|
||||
* @param \Illuminate\Support\Collection|null $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function formatForTree(null|\Illuminate\Support\Collection $data): array
|
||||
{
|
||||
if (empty($data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$tree = new Tree($data->toArray());
|
||||
return $tree->getTree();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\listener;
|
||||
|
||||
use app\adminapi\event\OperationLogEvent;
|
||||
use app\dao\logs\OperateLogDao;
|
||||
use support\Container;
|
||||
|
||||
/**
|
||||
* 操作日志监听器
|
||||
*
|
||||
* 处理操作日志事件,将操作记录保存到数据库
|
||||
*/
|
||||
class OperationLogListener
|
||||
{
|
||||
/**
|
||||
* 处理操作日志事件
|
||||
*
|
||||
* @param OperationLogEvent $event
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle(OperationLogEvent $event): void
|
||||
{
|
||||
// 保存操作日志到数据库
|
||||
$this->saveOperateLog($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存操作日志
|
||||
*
|
||||
* @param OperationLogEvent $event
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function saveOperateLog(OperationLogEvent $event): void
|
||||
{
|
||||
/** @var OperateLogDao $dao */
|
||||
$dao = Container::make(OperateLogDao::class);
|
||||
|
||||
$dao->save([
|
||||
'name' => $event->name,
|
||||
'app' => $event->app,
|
||||
'ip' => $event->ip,
|
||||
'ip_location' => $event->ipLocation,
|
||||
'browser' => $event->browser,
|
||||
'os' => $event->os,
|
||||
'url' => $event->url,
|
||||
'class_name' => $event->className,
|
||||
'action' => $event->action,
|
||||
'method' => $event->method,
|
||||
'param' => $event->param,
|
||||
'result' => is_array($event->result) ? json_encode($event->result, JSON_UNESCAPED_UNICODE) : $event->result,
|
||||
'user_name' => $event->userName,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\listener;
|
||||
|
||||
use app\adminapi\event\PointsChangedEvent;
|
||||
use app\adminapi\event\MemberLevelUpdatedEvent;
|
||||
use app\dao\member\MemberDao;
|
||||
use app\dao\member\MemberLevelDao;
|
||||
use app\dao\member\MemberPointsDao;
|
||||
use app\model\member\MemberLevel;
|
||||
use app\enum\member\PointType;
|
||||
use app\enum\common\EnabledStatus;
|
||||
use support\Container;
|
||||
|
||||
/**
|
||||
* 积分变动监听器
|
||||
*
|
||||
* 处理积分变动的事件:
|
||||
* 1. 更新会员积分
|
||||
* 2. 记录积分流水
|
||||
* 3. 触发会员等级更新事件
|
||||
*/
|
||||
class PointsChangedListener
|
||||
{
|
||||
/**
|
||||
* 处理积分变动事件
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle(PointsChangedEvent $event): void
|
||||
{
|
||||
// 更新会员积分
|
||||
$this->updateMemberPoints($event);
|
||||
|
||||
// 记录积分流水
|
||||
$this->recordPointsLog($event);
|
||||
|
||||
// 触发会员等级更新事件
|
||||
$this->updateMemberLevel($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新会员积分
|
||||
*/
|
||||
private function updateMemberPoints(PointsChangedEvent $event): void
|
||||
{
|
||||
/** @var MemberDao $dao */
|
||||
$dao = Container::make(MemberDao::class);
|
||||
$member = $dao->get($event->memberId);
|
||||
|
||||
if (!$member) {
|
||||
return;
|
||||
}
|
||||
|
||||
$member->points = $event->newPoints;
|
||||
$member->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录积分流水
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function recordPointsLog(PointsChangedEvent $event): void
|
||||
{
|
||||
/** @var MemberPointsDao $dao */
|
||||
$dao = Container::make(MemberPointsDao::class);
|
||||
|
||||
$dao->save([
|
||||
'member_id' => $event->memberId,
|
||||
'type' => $event->type->value,
|
||||
'points' => $event->points,
|
||||
'balance' => $event->newPoints,
|
||||
'remark' => $event->remark,
|
||||
'related_id' => $event->relatedId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新会员等级
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function updateMemberLevel(PointsChangedEvent $event): void
|
||||
{
|
||||
$memberId = $event->memberId;
|
||||
$newPoints = $event->newPoints;
|
||||
|
||||
// 获取会员对象
|
||||
/** @var MemberDao $dao */
|
||||
$dao = Container::make(MemberDao::class);
|
||||
$member = $dao->get($memberId);
|
||||
|
||||
if (!$member) {
|
||||
return;
|
||||
}
|
||||
|
||||
$oldLevelId = $member->level_id;
|
||||
$oldLevelName = null;
|
||||
|
||||
/** @var MemberLevelDao $memberLevelDao */
|
||||
$memberLevelDao = Container::make(MemberLevelDao::class);
|
||||
// 获取旧等级名称
|
||||
if ($oldLevelId) {
|
||||
$oldLevel = $memberLevelDao->get($oldLevelId);
|
||||
if ($oldLevel) {
|
||||
$oldLevelName = $oldLevel->name;
|
||||
}
|
||||
}
|
||||
|
||||
// 根据新积分获取等级
|
||||
$newLevel = $memberLevelDao->query()
|
||||
->where('enabled', EnabledStatus::ENABLED->value)
|
||||
->where('min_points', '<=', $newPoints)
|
||||
->where(function ($query) use ($newPoints) {
|
||||
$query->where('max_points', '>=', $newPoints)
|
||||
->orWhere('max_points', 0);
|
||||
})
|
||||
->orderBy('level', 'desc')
|
||||
->first();
|
||||
|
||||
// 如果等级存在且与当前等级不同,更新等级
|
||||
if ($newLevel && $newLevel->id != $member->level_id) {
|
||||
$member->level_id = $newLevel->id;
|
||||
$member->save();
|
||||
|
||||
$levelEvent = new MemberLevelUpdatedEvent(
|
||||
$memberId,
|
||||
$oldLevelId,
|
||||
$newLevel->id,
|
||||
$oldLevelName,
|
||||
$newLevel->name,
|
||||
$event->type === PointType::INCREASE ? 'points_increase' : 'points_deducted',
|
||||
''
|
||||
);
|
||||
$levelEvent->dispatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\listener;
|
||||
|
||||
use app\adminapi\event\ReviewApprovedEvent;
|
||||
use app\enum\review\ReviewStatus;
|
||||
use app\model\review\Review;
|
||||
use support\Log;
|
||||
|
||||
/**
|
||||
* 审核通过事件监听器
|
||||
*/
|
||||
class ReviewApprovedListener
|
||||
{
|
||||
/**
|
||||
* 处理审核通过事件
|
||||
*
|
||||
* @param ReviewApprovedEvent $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(ReviewApprovedEvent $event): void
|
||||
{
|
||||
try {
|
||||
$review = $event->review;
|
||||
|
||||
// 记录日志
|
||||
Log::channel('default')->info('审核通过', [
|
||||
'review_id' => $review->id,
|
||||
'reviewable_type' => $review->reviewable_type,
|
||||
'reviewable_id' => $review->reviewable_id,
|
||||
'reviewer_id' => $review->reviewer_id,
|
||||
]);
|
||||
|
||||
// 根据不同的审核类型处理业务逻辑
|
||||
$this->handleReviewable($review, ReviewStatus::APPROVED->value);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
Log::channel('default')->error('审核通过事件处理失败', [
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新审核记录状态
|
||||
*
|
||||
* @param Review $review 审核记录
|
||||
* @param int $status 审核状态
|
||||
*/
|
||||
protected function handleReviewable(Review $review, int $status): void
|
||||
{
|
||||
$review->status = $status;
|
||||
$review->reviewed_at = time();
|
||||
$review->save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\listener;
|
||||
|
||||
use app\adminapi\event\ReviewCreatedEvent;
|
||||
use support\Log;
|
||||
|
||||
/**
|
||||
* 审核创建事件监听器
|
||||
*/
|
||||
class ReviewCreatedListener
|
||||
{
|
||||
/**
|
||||
* 处理审核创建事件
|
||||
*
|
||||
* @param ReviewCreatedEvent $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(ReviewCreatedEvent $event): void
|
||||
{
|
||||
try {
|
||||
$review = $event->review;
|
||||
|
||||
// 记录日志
|
||||
Log::channel('default')->info('创建审核记录', [
|
||||
'review_id' => $review->id,
|
||||
'reviewable_type' => $review->reviewable_type,
|
||||
'reviewable_id' => $review->reviewable_id,
|
||||
]);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
Log::channel('default')->error('审核创建事件处理失败', [
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\listener;
|
||||
|
||||
use app\adminapi\event\ReviewRejectedEvent;
|
||||
use app\model\review\Review;
|
||||
use support\Log;
|
||||
|
||||
/**
|
||||
* 审核拒绝事件监听器
|
||||
*/
|
||||
class ReviewRejectedListener
|
||||
{
|
||||
/**
|
||||
* 处理审核拒绝事件
|
||||
*
|
||||
* @param ReviewRejectedEvent $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(ReviewRejectedEvent $event): void
|
||||
{
|
||||
try {
|
||||
$review = $event->review;
|
||||
$reason = $event->data['reason'] ?? '';
|
||||
|
||||
// 记录日志
|
||||
Log::channel('default')->info('审核拒绝', [
|
||||
'review_id' => $review->id,
|
||||
'reviewable_type' => $review->reviewable_type,
|
||||
'reviewable_id' => $review->reviewable_id,
|
||||
'reviewer_id' => $review->reviewer_id,
|
||||
'reason' => $reason,
|
||||
]);
|
||||
|
||||
// 根据不同的审核类型处理业务逻辑
|
||||
$this->handleReviewable($review, 2, $reason);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
Log::channel('default')->error('审核拒绝事件处理失败', [
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新审核记录状态
|
||||
*
|
||||
* @param Review $review 审核记录
|
||||
* @param int $status 审核状态
|
||||
* @param string $reason 拒绝原因
|
||||
*/
|
||||
protected function handleReviewable(Review $review, int $status, string $reason = ''): void
|
||||
{
|
||||
$review->status = $status;
|
||||
$review->reviewed_at = time();
|
||||
$review->reason = $reason;
|
||||
$review->save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\middleware;
|
||||
|
||||
|
||||
use app\adminapi\middleware\helper\SseHelper;
|
||||
use core\exception\handler\UnauthorizedHttpException;
|
||||
use core\jwt\JwtToken;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\attribute\AllowAnonymous;
|
||||
use madong\swagger\helper\AnnotationHelper;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
use Webman\MiddlewareInterface;
|
||||
|
||||
/**
|
||||
* AccessToken 中间件(JWT Token 验证)
|
||||
* 功能:
|
||||
* 1. 检查请求是否需要跳过 Token 验证(通过 AllowAnonymous 注解)
|
||||
* 2. 验证 JWT Token 有效性,无效则返回 401
|
||||
*/
|
||||
#\[\Attribute\]
|
||||
final class AccessTokenMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public function process(Request $request, callable $handler): Response
|
||||
{
|
||||
$route = $request->route;
|
||||
if (!$route || !isset($request->action)) {
|
||||
return $handler($request);
|
||||
}
|
||||
|
||||
$controllerClass = $request->controller;
|
||||
$action = $request->action;
|
||||
|
||||
$skipAuth = AnnotationHelper::getMethodAnnotation($controllerClass, $action, AllowAnonymous::class);
|
||||
if ($skipAuth && !$skipAuth->requireToken) {
|
||||
return $handler($request);
|
||||
}
|
||||
|
||||
try {
|
||||
$jwt = new JwtToken();
|
||||
$payload = $jwt->verify();
|
||||
$userId = $payload['id'] ?? null;
|
||||
if (empty($userId)) {
|
||||
throw new UnauthorizedHttpException();
|
||||
}
|
||||
$jwt->touchCurrentToken();
|
||||
} catch (\Exception $e) {
|
||||
// 检查是否为SSE请求
|
||||
if (SseHelper::isSseRequest($request)) {
|
||||
return SseHelper::sendSseErrorViaConnection($request, $e->getMessage());
|
||||
}
|
||||
return Json::fail($e->getMessage(), [], 401);
|
||||
}
|
||||
return $handler($request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\middleware;
|
||||
|
||||
use app\adminapi\CurrentUser;
|
||||
use app\adminapi\event\OperationLogEvent;
|
||||
use madong\helper\Arr;
|
||||
use madong\swagger\attribute\AllowAnonymous;
|
||||
use madong\swagger\helper\AnnotationHelper;
|
||||
use support\Container;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
use Webman\MiddlewareInterface;
|
||||
|
||||
#[\Attribute]
|
||||
final class OperationMiddleware implements MiddlewareInterface
|
||||
{
|
||||
|
||||
private CurrentUser $currentUser;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->currentUser = Container::make(CurrentUser::class);
|
||||
}
|
||||
|
||||
public function process(Request $request, callable $handler): Response
|
||||
{
|
||||
$controllerClass = $request->controller;
|
||||
$action = $request->action;
|
||||
|
||||
$oaAnnotations = AnnotationHelper::getMethodOaAnnotations($controllerClass, $action);
|
||||
$oaData = $this->extractOaData($oaAnnotations);
|
||||
$firstOa = Arr::first($oaData);
|
||||
|
||||
$response = $handler($request);
|
||||
|
||||
// 检查是否需要记录操作日志(跳过匿名访问)
|
||||
$allowAnonymous = AnnotationHelper::getMethodAnnotation($controllerClass, $action, AllowAnonymous::class);
|
||||
if (!$allowAnonymous || $allowAnonymous->requirePermission) {
|
||||
$logData = $this->buildLogData($request, $response, $firstOa);
|
||||
$event = new OperationLogEvent(
|
||||
$logData['name'],
|
||||
$logData['oa_tags'],
|
||||
$logData['oa_description'],
|
||||
$logData['app'],
|
||||
$logData['ip'],
|
||||
$logData['ip_location'],
|
||||
$logData['browser'],
|
||||
$logData['os'],
|
||||
$logData['url'],
|
||||
$logData['class_name'],
|
||||
$logData['action'],
|
||||
$logData['method'],
|
||||
$logData['param'],
|
||||
$logData['result'],
|
||||
$logData['user_name'],
|
||||
$logData['user_id']
|
||||
);
|
||||
$event->dispatch();
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function extractOaData(array $oaAnnotations): array
|
||||
{
|
||||
return array_map(function ($annotation) {
|
||||
return [
|
||||
'type' => $annotation::class, // 注解类型(如 OpenApi\Attributes\Get)
|
||||
'summary' => $annotation->summary, // 操作摘要
|
||||
'tags' => $annotation->tags, // 标签
|
||||
'description' => $annotation->description ?? '', // 描述
|
||||
];
|
||||
}, $oaAnnotations);
|
||||
}
|
||||
|
||||
private function buildLogData(Request $request, Response $response, ?array $firstOa): array
|
||||
{
|
||||
$adminInfo = $this->currentUser->admin();
|
||||
return [
|
||||
'name' => $firstOa['summary'] ?? '未知操作', // 用 OA summary 作为操作名称
|
||||
'oa_tags' => $firstOa['tags'] ?? [], // OA 标签
|
||||
'oa_description' => $firstOa['description'] ?? '', // OA 描述
|
||||
'app' => $request->app,
|
||||
'ip' => $request->getRealIp(),
|
||||
'ip_location' => $this->getIpLocation($request->getRealIp()), // 可选:IP 归属地
|
||||
'browser' => $this->getBrowser($request->header('user-agent')),
|
||||
'os' => $this->getOs($request->header('user-agent')),
|
||||
'url' => trim($request->path()),
|
||||
'class_name' => $request->controller,
|
||||
'action' => $request->action,
|
||||
'method' => $request->method(),
|
||||
'param' => $this->filterParams($request->all()), // 过滤后的参数(数组)
|
||||
'result' => $this->formatResponse($response), // 格式化响应(数组/字符串)
|
||||
'user_name' => $adminInfo['user_name'] ?? '匿名用户',
|
||||
'user_id' => $adminInfo['user_id'] ?? 0,
|
||||
];
|
||||
}
|
||||
|
||||
private function getIpLocation(string $ip): string
|
||||
{
|
||||
return '未知';
|
||||
}
|
||||
|
||||
private function getBrowser($user_agent): string
|
||||
{
|
||||
$br = 'Unknown';
|
||||
if (preg_match('/MSIE/i', $user_agent)) {
|
||||
$br = 'MSIE';
|
||||
} elseif (preg_match('/Firefox/i', $user_agent)) {
|
||||
$br = 'Firefox';
|
||||
} elseif (preg_match('/Chrome/i', $user_agent)) {
|
||||
$br = 'Chrome';
|
||||
} elseif (preg_match('/Safari/i', $user_agent)) {
|
||||
$br = 'Safari';
|
||||
} elseif (preg_match('/Opera/i', $user_agent)) {
|
||||
$br = 'Opera';
|
||||
} else {
|
||||
$br = 'Other';
|
||||
}
|
||||
return $br;
|
||||
}
|
||||
|
||||
private function getOs($user_agent): string
|
||||
{
|
||||
$os = 'Unknown';
|
||||
if (preg_match('/win/i', $user_agent)) {
|
||||
$os = 'Windows';
|
||||
} elseif (preg_match('/mac/i', $user_agent)) {
|
||||
$os = 'Mac';
|
||||
} elseif (preg_match('/linux/i', $user_agent)) {
|
||||
$os = 'Linux';
|
||||
} else {
|
||||
$os = 'Other';
|
||||
}
|
||||
return $os;
|
||||
}
|
||||
|
||||
private function filterParams($params): string
|
||||
{
|
||||
$blackList = ['password', 'old_password', 'new_password', 'content'];
|
||||
foreach ($params as $key => $value) {
|
||||
if (in_array($key, $blackList)) {
|
||||
$params[$key] = '******';
|
||||
}
|
||||
}
|
||||
return json_encode($params, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
private function formatResponse(Response $response): array|string
|
||||
{
|
||||
$rawBody = $response->rawBody();
|
||||
if (str_contains($rawBody, 'json')) {
|
||||
return json_decode($rawBody, true) ?? $rawBody;
|
||||
}
|
||||
return $rawBody;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\middleware;
|
||||
|
||||
use app\adminapi\middleware\helper\SseHelper;
|
||||
use app\adminapi\CurrentUser;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use core\exception\handler\UnauthorizedHttpException;
|
||||
use core\jwt\JwtToken;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\attribute\AllowAnonymous;
|
||||
use madong\swagger\helper\AnnotationHelper;
|
||||
use support\Container;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
use Webman\MiddlewareInterface;
|
||||
|
||||
/**
|
||||
* 方法级权限校验中间件
|
||||
* 功能:
|
||||
* 1. 支持 SkipAuth 注解跳过权限校验
|
||||
* 2. 校验类/方法级的 Permission 注解
|
||||
* 3. 处理 AND/OR 操作类型的权限逻辑
|
||||
*/
|
||||
#\[\Attribute\]
|
||||
final class PermissionMiddleware implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* Webman 中间件入口
|
||||
*
|
||||
* @throws \core\exception\handler\ForbiddenHttpException|\core\exception\handler\UnauthorizedHttpException
|
||||
*/
|
||||
public function process(Request $request, callable $handler): Response
|
||||
{
|
||||
|
||||
$controllerClass = $request->controller;
|
||||
$action = $request->action;
|
||||
$allowAnonymous = AnnotationHelper::getMethodAnnotation($controllerClass, $action, AllowAnonymous::class);
|
||||
if ($allowAnonymous && !$allowAnonymous->requirePermission) {
|
||||
return $handler($request);
|
||||
}
|
||||
try {
|
||||
$userId = (new JwtToken())->id();
|
||||
if (empty($userId)) {
|
||||
throw new UnauthorizedHttpException();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 检查是否为SSE请求
|
||||
if (SseHelper::isSseRequest($request)) {
|
||||
return SseHelper::sendSseErrorViaConnection($request, $e->getMessage());
|
||||
}
|
||||
return Json::fail($e->getMessage(), [], 401);
|
||||
}
|
||||
// 使用CurrentUser进行权限验证
|
||||
$currentUser = Container::make(CurrentUser::class);
|
||||
|
||||
// 顶级管理员直接跳过权限验证
|
||||
if ($currentUser->isSuperAdmin()) {
|
||||
return $handler($request);
|
||||
}
|
||||
|
||||
try {
|
||||
//权限验证
|
||||
$permissions = $this->resolvePermissions($controllerClass, $action);
|
||||
$this->validatePermissions($currentUser, $permissions);
|
||||
} catch (\Exception $e) {
|
||||
// 检查是否为SSE请求
|
||||
if (SseHelper::isSseRequest($request)) {
|
||||
return SseHelper::sendSseErrorViaConnection($request, $e->getMessage());
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $handler($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析类/方法级的 Permission 注解
|
||||
*
|
||||
* @return array<Permission> 合并后的权限注解列表
|
||||
*/
|
||||
private function resolvePermissions(string $controllerClass, string $action): array
|
||||
{
|
||||
$permissions = [];
|
||||
|
||||
// a. 类级别注解(作用于整个控制器)
|
||||
$classAnnotation = AnnotationHelper::getClassAnnotation($controllerClass, Permission::class);
|
||||
if ($classAnnotation) {
|
||||
$permissions[] = $classAnnotation;
|
||||
}
|
||||
|
||||
// b. 方法级别注解(覆盖/补充类级别规则)
|
||||
$methodAnnotation = AnnotationHelper::getMethodAnnotation($controllerClass, $action, Permission::class);
|
||||
if ($methodAnnotation) {
|
||||
$permissions[] = $methodAnnotation;
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验用户权限是否符合注解规则
|
||||
*
|
||||
* @throws \core\exception\handler\ForbiddenHttpException
|
||||
*/
|
||||
private function validatePermissions(CurrentUser $currentUser, array $permissions): void
|
||||
{
|
||||
foreach ($permissions as $permission) {
|
||||
$this->handleSinglePermission($currentUser, $permission);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 Permission 注解的权限校验逻辑
|
||||
*
|
||||
* @param CurrentUser $currentUser 当前用户
|
||||
* @param Permission $permission 权限注解
|
||||
*
|
||||
* @throws \core\exception\handler\ForbiddenHttpException
|
||||
*/
|
||||
private function handleSinglePermission(
|
||||
CurrentUser $currentUser,
|
||||
Permission $permission
|
||||
): void
|
||||
{
|
||||
// 权限码数组
|
||||
$codes = $permission->getCodes();
|
||||
// 操作类型
|
||||
$operation = $permission->getOperation();
|
||||
|
||||
// 使用CurrentUser的checkPermission方法进行权限验证
|
||||
$currentUser->checkPermission($codes, $operation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\middleware\helper;
|
||||
|
||||
use core\tool\Sse;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
|
||||
/**
|
||||
* SSE 处理辅助类
|
||||
* 提供 SSE 请求检测和错误处理功能
|
||||
*/
|
||||
class SseHelper
|
||||
{
|
||||
/**
|
||||
* 检查是否为SSE请求
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSseRequest(Request $request): bool
|
||||
{
|
||||
// 方法1:检查请求头
|
||||
$acceptHeader = $request->header('Accept', '');
|
||||
if (str_contains($acceptHeader, 'text/event-stream')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 方法2:检查路径(SSE接口通常有特定路径模式)
|
||||
$path = $request->path();
|
||||
$ssePaths = [
|
||||
'/terminal',
|
||||
'/terminal/exec',
|
||||
];
|
||||
foreach ($ssePaths as $ssePath) {
|
||||
if (str_starts_with($path, $ssePath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 方法3:检查查询参数
|
||||
$isSse = $request->input('sse', false) || $request->input('stream', false);
|
||||
if ($isSse) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过连接发送SSE错误消息
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $message
|
||||
*
|
||||
* @return Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function sendSseErrorViaConnection(Request $request, string $message): Response
|
||||
{
|
||||
// 检查是否有连接对象
|
||||
if (!property_exists($request, 'connection') || !$request->connection) {
|
||||
// 回退到普通的SSE响应
|
||||
return self::createSseErrorResponse($message, $request->input('uuid'));
|
||||
}
|
||||
|
||||
$connection = $request->connection;
|
||||
$uuid = $request->input('uuid');
|
||||
|
||||
try {
|
||||
// 发送SSE响应头
|
||||
$connection->send(new Response(200, [
|
||||
'Content-Type' => 'text/event-stream',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Connection' => 'keep-alive',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Expose-Headers' => 'Content-Type',
|
||||
], "\r\n"));
|
||||
|
||||
// 发送SSE错误消息,传递uuid
|
||||
$connection->send(Sse::error($message, null, $uuid));
|
||||
|
||||
// 发送空数据表示结束,防止前端等待更多数据
|
||||
$connection->send(new Response(200, [], ""));
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// 忽略连接异常,返回普通响应
|
||||
return self::createSseErrorResponse($message, $uuid);
|
||||
}
|
||||
|
||||
// 返回空响应结束请求
|
||||
return new Response(200, [], '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建SSE格式的错误响应
|
||||
*
|
||||
* @param string $message
|
||||
* @param string|null $uuid
|
||||
*
|
||||
* @return Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function createSseErrorResponse(string $message, ?string $uuid = null): Response
|
||||
{
|
||||
$errorMessage = Sse::error($message, [], $uuid);
|
||||
|
||||
// 创建完整的SSE响应
|
||||
$responseBody = $errorMessage;
|
||||
|
||||
return new Response(200, [
|
||||
'Content-Type' => 'text/event-stream',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Connection' => 'keep-alive',
|
||||
'X-Accel-Buffering' => 'no', // 禁用Nginx缓冲
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Expose-Headers' => 'Content-Type',
|
||||
], $responseBody);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\schema\request;
|
||||
|
||||
use madong\swagger\schema\BaseRequestDTO;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '第三方登录请求',
|
||||
description: '第三方登录请求参数'
|
||||
)]
|
||||
class ThirdPartyLoginRequest extends BaseRequestDTO
|
||||
{
|
||||
#[OA\Property(
|
||||
description: '账套',
|
||||
type: 'string',
|
||||
example: '669e3705b3d7de'
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:50')]
|
||||
public string $acct_id;
|
||||
|
||||
#[OA\Property(
|
||||
description: '授权应用ID',
|
||||
type: 'string',
|
||||
example: '285191_1fbo28uL6prUQ90J5YTP2dSL6vx71tqv'
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:100')]
|
||||
public string $app_id;
|
||||
|
||||
#[OA\Property(
|
||||
description: '应用秘钥',
|
||||
type: 'string',
|
||||
example: '8439b36501df4f6185f521781d6b7328'
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:100')]
|
||||
public string $app_secret;
|
||||
|
||||
#[OA\Property(
|
||||
description: '授权登录用户名',
|
||||
type: 'string',
|
||||
example: 'admin'
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:50')]
|
||||
public string $user_name;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
|
||||
namespace app\adminapi\schema\request\crontab;
|
||||
|
||||
use madong\swagger\schema\BaseResponseDTO;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '定时任务执行请求',
|
||||
description: '定时任务手动执行接口参数'
|
||||
)]
|
||||
class CrontabExecuteRequest extends BaseResponseDTO
|
||||
{
|
||||
#[OA\Property(
|
||||
description: '任务ID',
|
||||
type: 'string',
|
||||
example: 1
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|integer|min:1')]
|
||||
public int|string $data;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\schema\request\crontab;
|
||||
|
||||
use app\schema\request\BaseFormRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '定时任务表单',
|
||||
description: '定时任务创建和编辑接口共用的表单请求参数'
|
||||
)]
|
||||
class CrontabFormRequest extends BaseFormRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
property: 'type',
|
||||
description: '任务类型',
|
||||
type: 'integer',
|
||||
example: 1
|
||||
)]
|
||||
public int $type;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'task_cycle',
|
||||
description: '任务周期',
|
||||
type: 'integer',
|
||||
example: 1
|
||||
)]
|
||||
public int $task_cycle;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'singleton',
|
||||
description: '是否单例执行',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1
|
||||
)]
|
||||
public int $singleton;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'enabled',
|
||||
description: '是否启用',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 0
|
||||
)]
|
||||
public int $enabled;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'title',
|
||||
description: '任务标题',
|
||||
type: 'string',
|
||||
example: '数据备份任务'
|
||||
)]
|
||||
public string $title;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'hour',
|
||||
description: '小时',
|
||||
type: 'integer',
|
||||
example: 1
|
||||
)]
|
||||
public int $hour;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'minute',
|
||||
description: '分钟',
|
||||
type: 'integer',
|
||||
example: 1
|
||||
)]
|
||||
public int $minute;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'target',
|
||||
description: '执行目标',
|
||||
type: 'string',
|
||||
example: 'app\command\Backup::run'
|
||||
)]
|
||||
public string $target;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\schema\request\crontab;
|
||||
|
||||
use app\schema\request\BaseQueryRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '定时任务日志查询请求',
|
||||
description: '定时任务日志列表查询过滤参数'
|
||||
)]
|
||||
class CrontabLogQueryRequest extends BaseQueryRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
property: 'id',
|
||||
description: '日志ID',
|
||||
type: 'string',
|
||||
example: '123456789012345678'
|
||||
)]
|
||||
public ?string $id = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'crontab_id',
|
||||
description: '定时任务ID',
|
||||
type: 'string',
|
||||
example: '987654321098765432'
|
||||
)]
|
||||
public ?string $crontab_id = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'target',
|
||||
description: '执行目标',
|
||||
type: 'string',
|
||||
example: 'app\command\Backup::run'
|
||||
)]
|
||||
public ?string $target = null;
|
||||
|
||||
|
||||
#[OA\Property(
|
||||
property: 'return_code',
|
||||
description: '返回码',
|
||||
type: 'integer',
|
||||
example: 0
|
||||
)]
|
||||
public ?int $return_code = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'running_time',
|
||||
description: '运行时间(秒)',
|
||||
type: 'integer',
|
||||
example: 10
|
||||
)]
|
||||
public ?int $running_time = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'created_at',
|
||||
description: '创建时间',
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: '2024-05-12T10:30:00Z'
|
||||
)]
|
||||
public ?string $created_at = null;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\schema\request\crontab;
|
||||
|
||||
use app\schema\request\BaseQueryRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '定时任务列表查询请求',
|
||||
description: '定时任务列表查询过滤参数'
|
||||
)]
|
||||
class CrontabQueryRequest extends BaseQueryRequest
|
||||
{
|
||||
|
||||
|
||||
#[OA\Property(
|
||||
property: 'type',
|
||||
description: '任务类型',
|
||||
type: 'integer',
|
||||
example: 1
|
||||
)]
|
||||
public ?int $type = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'task_cycle',
|
||||
description: '任务周期',
|
||||
type: 'integer',
|
||||
example: 1
|
||||
)]
|
||||
public ?int $task_cycle = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'singleton',
|
||||
description: '是否单例执行',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1
|
||||
)]
|
||||
public ?int $singleton = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'enabled',
|
||||
description: '是否启用',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 0
|
||||
)]
|
||||
public ?int $enabled = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'title',
|
||||
description: '任务标题',
|
||||
type: 'string',
|
||||
example: '数据备份任务'
|
||||
)]
|
||||
public ?string $title = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'target',
|
||||
description: '执行目标',
|
||||
type: 'string',
|
||||
example: 'app\command\Backup::run'
|
||||
)]
|
||||
public ?string $target = null;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\schema\request\crontab;
|
||||
|
||||
|
||||
use madong\swagger\schema\BaseRequestDTO;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '定时任务状态变更请求',
|
||||
description: '暂停/恢复定时任务'
|
||||
)]
|
||||
class CrontabStatusRequest extends BaseRequestDTO
|
||||
{
|
||||
#[OA\Property(
|
||||
description: '任务ID',
|
||||
type: 'string',
|
||||
example: 1
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|integer|min:1')]
|
||||
public int|string $data;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\schema\request\dict;
|
||||
|
||||
use app\schema\request\BaseFormRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '字典表单',
|
||||
description: '字典创建和编辑接口共用的表单请求参数'
|
||||
)]
|
||||
class DictFormRequest extends BaseFormRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
description: '字典编码',
|
||||
type: 'string',
|
||||
example: 'status',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:50|unique:sys_dict,code')]
|
||||
public string $code;
|
||||
|
||||
#[OA\Property(
|
||||
description: '字典名称',
|
||||
type: 'string',
|
||||
example: '状态字典',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:50|unique:sys_dict,name')]
|
||||
public string $name;
|
||||
|
||||
#[OA\Property(
|
||||
description: '状态(0:禁用,1:启用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1,
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|in:0,1')]
|
||||
public int $status;
|
||||
|
||||
#[OA\Property(
|
||||
description: '排序号',
|
||||
type: 'integer',
|
||||
example: 100,
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|integer|min:0|max:999')]
|
||||
public int $sort;
|
||||
|
||||
#[OA\Property(
|
||||
description: '备注',
|
||||
type: 'string',
|
||||
example: '系统状态字典',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:255|nullable')]
|
||||
public ?string $remark = null;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\schema\request\dict;
|
||||
|
||||
|
||||
use app\schema\request\BaseFormRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '字典项表单',
|
||||
description: '字典项创建和编辑接口共用的表单请求参数'
|
||||
)]
|
||||
class DictItemFormRequest extends BaseFormRequest
|
||||
{
|
||||
|
||||
#[OA\Property(
|
||||
description: '字典ID',
|
||||
type: 'string',
|
||||
example: '242218273104986112',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string')]
|
||||
public string $dict_id;
|
||||
|
||||
#[OA\Property(
|
||||
description: '选项标签',
|
||||
type: 'string',
|
||||
example: '所属分组',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:50')]
|
||||
public string $label;
|
||||
|
||||
#[OA\Property(
|
||||
description: '选项值',
|
||||
type: 'string',
|
||||
example: 'default',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:50|unique:sys_dict_item,value,dict_id,' . 'id' . ',id')]
|
||||
public string $value;
|
||||
|
||||
#[OA\Property(
|
||||
description: '排序号',
|
||||
type: 'integer',
|
||||
example: 1,
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|integer|min:0|max:999')]
|
||||
public int $sort;
|
||||
|
||||
#[OA\Property(
|
||||
description: '状态(0:禁用,1:启用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1,
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|in:0,1')]
|
||||
public int $enabled;
|
||||
|
||||
#[OA\Property(
|
||||
description: '颜色值',
|
||||
type: 'string',
|
||||
example: '#4CAF50',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:20|nullable')]
|
||||
public ?string $color = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '扩展信息',
|
||||
type: 'object',
|
||||
nullable: true,
|
||||
example: '{"icon": "el-icon-setting"}'
|
||||
)]
|
||||
#[ValidationRules(rules: 'array|nullable')]
|
||||
public ?array $ext = null;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
namespace app\adminapi\schema\request\dict;
|
||||
|
||||
|
||||
use app\schema\request\BaseQueryRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '字典项列表查询请求',
|
||||
description: '字典项列表接口的查询过滤参数'
|
||||
)]
|
||||
class DictItemQueryRequest extends BaseQueryRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
description: '字典ID',
|
||||
type: 'string',
|
||||
example: '242218273104986112',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|nullable')]
|
||||
public ?string $dict_id = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '选项标签',
|
||||
type: 'string',
|
||||
example: '所属分组',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:50|nullable')]
|
||||
public ?string $label = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '选项值',
|
||||
type: 'string',
|
||||
example: 'default',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:50|nullable')]
|
||||
public ?string $value = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '状态(0:禁用,1:启用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1,
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'in:0,1|nullable')]
|
||||
public ?int $enabled = null;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\schema\request\dict;
|
||||
|
||||
|
||||
use app\schema\request\BaseQueryRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '字典列表查询请求',
|
||||
description: '字典列表接口的查询过滤参数'
|
||||
)]
|
||||
class DictQueryRequest extends BaseQueryRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
description: '字典编码',
|
||||
type: 'string',
|
||||
example: 'status',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:50|nullable')]
|
||||
public ?string $code = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '字典名称',
|
||||
type: 'string',
|
||||
example: '状态字典',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:50|nullable')]
|
||||
public ?string $name = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '状态(0:禁用,1:启用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1,
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'in:0,1|nullable')]
|
||||
public ?int $status = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '排序字段',
|
||||
type: 'string',
|
||||
default: 'sort asc',
|
||||
example: 'sort asc'
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|nullable')]
|
||||
public ?string $order = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '返回数据格式(normal|tree|select)',
|
||||
type: 'string',
|
||||
default: 'normal',
|
||||
enum: ['normal', 'tree', 'select'],
|
||||
example: 'normal'
|
||||
)]
|
||||
#[ValidationRules(rules: 'in:normal,tree,select|nullable')]
|
||||
public ?string $format = null;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\schema\request\gateway;
|
||||
|
||||
|
||||
use app\schema\request\BaseFormRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '限访规则表单',
|
||||
description: '限访规则创建和编辑接口共用的表单请求参数'
|
||||
)]
|
||||
class RateLimiterFormRequest extends BaseFormRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
property: 'name',
|
||||
description: '规则名称',
|
||||
type: 'string',
|
||||
example: 'customized'
|
||||
)]
|
||||
public string $name;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'match_type',
|
||||
description: '匹配类型',
|
||||
type: 'string',
|
||||
example: 'allow'
|
||||
)]
|
||||
public string $match_type;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'methods',
|
||||
description: '请求方法(多个用逗号分隔,如:GET,POST)',
|
||||
type: 'string',
|
||||
example: 'GET'
|
||||
)]
|
||||
public string $methods;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'path',
|
||||
description: '请求路径',
|
||||
type: 'string',
|
||||
example: '/system/menu'
|
||||
)]
|
||||
public string $path;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'limit_value',
|
||||
description: '限制次数',
|
||||
type: 'integer',
|
||||
example: 60
|
||||
)]
|
||||
public int $limit_value;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'period',
|
||||
description: '限制周期(秒)',
|
||||
type: 'integer',
|
||||
example: 1
|
||||
)]
|
||||
public int $period;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'enabled',
|
||||
description: '是否启用(0:禁用,1:启用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1
|
||||
)]
|
||||
public int $enabled;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'message',
|
||||
description: '触发限制时提示消息',
|
||||
type: 'string',
|
||||
example: '请求频繁'
|
||||
)]
|
||||
public string $message;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
namespace app\adminapi\schema\request\gateway;
|
||||
|
||||
use app\schema\request\BaseQueryRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '限访规则列表查询请求',
|
||||
description: '限访规则接口查询过滤参数'
|
||||
)]
|
||||
class RateLimiterQueryRequest extends BaseQueryRequest
|
||||
{
|
||||
|
||||
#[OA\Property(
|
||||
property: 'id',
|
||||
description: '规则ID',
|
||||
type: 'string',
|
||||
example: '247298887944531968',
|
||||
nullable: true
|
||||
)]
|
||||
public ?string $id = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'name',
|
||||
description: '规则名称(模糊匹配)',
|
||||
type: 'string',
|
||||
example: 'customized',
|
||||
nullable: true
|
||||
)]
|
||||
public ?string $name = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'match_type',
|
||||
description: '匹配类型',
|
||||
type: 'string',
|
||||
example: 'allow',
|
||||
nullable: true
|
||||
)]
|
||||
public ?string $match_type = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'methods',
|
||||
description: '请求方法',
|
||||
type: 'string',
|
||||
example: 'GET',
|
||||
nullable: true
|
||||
)]
|
||||
public ?string $methods = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'path',
|
||||
description: '请求路径(模糊匹配)',
|
||||
type: 'string',
|
||||
example: '/system/menu',
|
||||
nullable: true
|
||||
)]
|
||||
public ?string $path = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'enabled',
|
||||
description: '是否启用(0:禁用,1:启用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1,
|
||||
nullable: true
|
||||
)]
|
||||
public ?int $enabled = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'limit_value',
|
||||
description: '限制次数',
|
||||
type: 'integer',
|
||||
example: 60,
|
||||
nullable: true
|
||||
)]
|
||||
public ?int $limit_value = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'period',
|
||||
description: '限制周期(秒)',
|
||||
type: 'integer',
|
||||
example: 1,
|
||||
nullable: true
|
||||
)]
|
||||
public ?int $period = null;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\schema\request\gateway;
|
||||
|
||||
use app\schema\request\BaseFormRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '限访名单表单',
|
||||
description: '限访名单创建和编辑接口共用的表单请求参数'
|
||||
)]
|
||||
class RateRestrictionsFormRequest extends BaseFormRequest
|
||||
{
|
||||
|
||||
#[OA\Property(
|
||||
property: 'name',
|
||||
description: '名单名称',
|
||||
type: 'string',
|
||||
example: ""
|
||||
)]
|
||||
public string $name;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'ip',
|
||||
description: 'IP地址(留空表示不限IP)',
|
||||
type: 'string',
|
||||
example: null,
|
||||
nullable: true
|
||||
)]
|
||||
public ?string $ip = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'enabled',
|
||||
description: '是否启用(0:禁用,1:启用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1
|
||||
)]
|
||||
public int $enabled;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'priority',
|
||||
description: '优先级(数值越大优先级越高)',
|
||||
type: 'integer',
|
||||
example: 100
|
||||
)]
|
||||
public int $priority;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'methods',
|
||||
description: '请求方法(多个用逗号分隔,如:GET,POST)',
|
||||
type: 'string',
|
||||
example: "GET"
|
||||
)]
|
||||
public string $methods;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'path',
|
||||
description: '请求路径',
|
||||
type: 'string',
|
||||
example: "/system/menu"
|
||||
)]
|
||||
public string $path;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'message',
|
||||
description: '限制提示消息',
|
||||
type: 'string',
|
||||
example: "限制访问"
|
||||
)]
|
||||
public string $message;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'start_time',
|
||||
description: '生效开始时间(UTC格式,如:2025-11-13T00:00:00Z)',
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: null,
|
||||
nullable: true
|
||||
)]
|
||||
public ?string $start_time = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'end_time',
|
||||
description: '生效结束时间(UTC格式,如:2025-11-14T00:00:00Z)',
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: null,
|
||||
nullable: true
|
||||
)]
|
||||
public ?string $end_time = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'remark',
|
||||
description: '备注信息',
|
||||
type: 'string',
|
||||
example: null,
|
||||
nullable: true
|
||||
)]
|
||||
public ?string $remark = null;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
namespace app\adminapi\schema\request\gateway;
|
||||
|
||||
use app\schema\request\BaseQueryRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '限访名单列表查询请求',
|
||||
description: '限访名单列表接口查询过滤参数'
|
||||
)]
|
||||
class RateRestrictionsQueryRequest extends BaseQueryRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
property: 'id',
|
||||
description: '名单ID',
|
||||
type: 'string',
|
||||
example: "247306532294254592",
|
||||
nullable: true
|
||||
)]
|
||||
public ?string $id = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'name',
|
||||
description: '名单名称(模糊匹配)',
|
||||
type: 'string',
|
||||
example: "sss",
|
||||
nullable: true
|
||||
)]
|
||||
public ?string $name = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'ip',
|
||||
description: 'IP地址(模糊匹配)',
|
||||
type: 'string',
|
||||
example: null,
|
||||
nullable: true
|
||||
)]
|
||||
public ?string $ip = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'enabled',
|
||||
description: '是否启用(0:禁用,1:启用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1,
|
||||
nullable: true
|
||||
)]
|
||||
public ?int $enabled = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'methods',
|
||||
description: '请求方法',
|
||||
type: 'string',
|
||||
example: "GET",
|
||||
nullable: true
|
||||
)]
|
||||
public ?string $methods = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'path',
|
||||
description: '请求路径(模糊匹配)',
|
||||
type: 'string',
|
||||
example: "/system/menu",
|
||||
nullable: true
|
||||
)]
|
||||
public ?string $path = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'priority',
|
||||
description: '优先级',
|
||||
type: 'integer',
|
||||
example: 100,
|
||||
nullable: true
|
||||
)]
|
||||
public ?int $priority = null;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\schema\request\login;
|
||||
|
||||
use madong\swagger\schema\BaseResponseDTO;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '登录请求',
|
||||
description: '登录请求参数'
|
||||
)]
|
||||
class LoginRequest extends BaseResponseDTO
|
||||
{
|
||||
|
||||
#[OA\Property(
|
||||
description: '用户名',
|
||||
type: 'string',
|
||||
example: 'admin',
|
||||
nullable: true,
|
||||
)]
|
||||
#[ValidationRules(rules: 'sometimes|string|max:50')]
|
||||
public ?string $user_name = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '密码',
|
||||
type: 'string',
|
||||
example: '123456',
|
||||
nullable: true,
|
||||
)]
|
||||
#[ValidationRules(rules: 'sometimes|string')]
|
||||
public ?string $password = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '手机号码',
|
||||
type: 'string',
|
||||
example: '18888888888',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'sometimes|mobile')]
|
||||
public ?string $mobile_phone = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '验证码',
|
||||
type: 'string',
|
||||
example: '1234',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'sometimes|string|length:4,6')]
|
||||
public ?string $code = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '验证码UUID',
|
||||
type: 'string',
|
||||
example: 'a3bb6c8c-9b12-467b-acbf-3a3c2ecd2d2f',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'sometimes|uuid')]
|
||||
public ?string $uuid = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '登录类型',
|
||||
type: 'string',
|
||||
default: 'admin',
|
||||
example: 'admin'
|
||||
)]
|
||||
#[ValidationRules(rules: 'default:admin|string|max:20')]
|
||||
public string $type = 'admin';
|
||||
|
||||
#[OA\Property(
|
||||
description: '授权类型',
|
||||
type: 'string',
|
||||
default: 'default',
|
||||
enum: ['default', 'sms', 'refresh_token'],
|
||||
example: 'default'
|
||||
)]
|
||||
#[ValidationRules(rules: 'default:default|in:default,sms,refresh_token')]
|
||||
public string $grant_type = 'default';
|
||||
|
||||
#[OA\Property(
|
||||
description: '公钥ID',
|
||||
type: 'string',
|
||||
example: '8119456a6fa4dfe32d01d84fc195ad23',
|
||||
nullable: true,
|
||||
)]
|
||||
#[ValidationRules(rules: 'sometimes|string|max:100')]
|
||||
public ?string $key_id = null;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
namespace app\adminapi\schema\request\logs;
|
||||
|
||||
use app\schema\request\BaseQueryRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '登录日志列表查询请求',
|
||||
description: '登录日志列表接口的查询过滤参数'
|
||||
)]
|
||||
class LoginLogQueryRequest extends BaseQueryRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
description: '用户名',
|
||||
type: 'string',
|
||||
example: 'admin',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:50|nullable')]
|
||||
public ?string $username = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '登录IP',
|
||||
type: 'string',
|
||||
example: '192.168.1.1',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:50|nullable')]
|
||||
public ?string $ipaddr = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '登录状态(0失败 1成功)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1,
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'in:0,1|nullable')]
|
||||
public ?int $status = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '开始时间',
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: '2025-01-01 00:00:00',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'date_format:Y-m-d H:i:s|nullable')]
|
||||
public ?string $start_time = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '结束时间',
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: '2025-01-31 23:59:59',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'date_format:Y-m-d H:i:s|nullable|after:start_time')]
|
||||
public ?string $end_time = null;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
namespace app\adminapi\schema\request\logs;
|
||||
|
||||
|
||||
use app\schema\request\BaseQueryRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '操作日志列表查询请求',
|
||||
description: '操作日志列表接口的查询过滤参数'
|
||||
)]
|
||||
class OperateLogQueryRequest extends BaseQueryRequest
|
||||
{
|
||||
|
||||
|
||||
#[OA\Property(
|
||||
property: 'user_name',
|
||||
description: '操作用户',
|
||||
type: 'string',
|
||||
example: 'admin',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:50|nullable')]
|
||||
public ?string $user_name = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'name',
|
||||
description: '操作名称',
|
||||
type: 'string',
|
||||
example: '数据字典',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:100|nullable')]
|
||||
public ?string $name = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'method',
|
||||
description: '请求方法',
|
||||
type: 'string',
|
||||
enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
|
||||
example: 'GET',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|in:GET,POST,PUT,DELETE,PATCH|nullable')]
|
||||
public ?string $method = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'ip',
|
||||
description: '操作IP',
|
||||
type: 'string',
|
||||
example: '220.165.172.45',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|ip|nullable')]
|
||||
public ?string $ip = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'start_date',
|
||||
description: '操作开始日期',
|
||||
type: 'string',
|
||||
format: 'date',
|
||||
example: '2025-10-01',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'date|nullable')]
|
||||
public ?string $start_date = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'end_date',
|
||||
description: '操作结束日期',
|
||||
type: 'string',
|
||||
format: 'date',
|
||||
example: '2025-10-31',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'date|after_or_equal:start_date|nullable')]
|
||||
public ?string $end_date = null;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\schema\request\member;
|
||||
|
||||
use app\schema\request\BaseFormRequest;
|
||||
|
||||
|
||||
/**
|
||||
* 会员表单请求DTO
|
||||
*/
|
||||
class MemberFormRequest extends BaseFormRequest
|
||||
{
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
public string $username;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
public ?string $email = null;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
public ?string $phone = null;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
public ?string $password = null;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
public ?string $nickname = null;
|
||||
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
public ?string $avatar = null;
|
||||
|
||||
/**
|
||||
* 等级ID
|
||||
*/
|
||||
public int $level_id = 1;
|
||||
|
||||
/**
|
||||
* 积分
|
||||
*/
|
||||
public int $points = 0;
|
||||
|
||||
/**
|
||||
* 余额
|
||||
*/
|
||||
public float $balance = 0;
|
||||
|
||||
/**
|
||||
* 性别
|
||||
*/
|
||||
public int $gender = 0;
|
||||
|
||||
/**
|
||||
* 生日
|
||||
*/
|
||||
public ?string $birthday = null;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
public int $status = 1;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\schema\request\member;
|
||||
|
||||
use app\schema\request\BaseQueryRequest;
|
||||
|
||||
|
||||
/**
|
||||
* 会员查询请求DTO
|
||||
*/
|
||||
class MemberQueryRequest extends BaseQueryRequest
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
public ?string $username = null;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
public ?string $email = null;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
public ?string $phone = null;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
public ?int $status = null;
|
||||
|
||||
/**
|
||||
* 等级ID
|
||||
*/
|
||||
public ?int $level_id = null;
|
||||
|
||||
/**
|
||||
* 排序字段
|
||||
*/
|
||||
public ?string $order_by = null;
|
||||
|
||||
/**
|
||||
* 排序方向
|
||||
*/
|
||||
public ?string $order_dir = null;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
namespace app\adminapi\schema\request\message;
|
||||
|
||||
|
||||
use app\schema\request\BaseFormRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '消息表单',
|
||||
description: '消息创建和编辑接口共用的表单请求参数'
|
||||
)]
|
||||
class MessageFormRequest extends BaseFormRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
property: 'type',
|
||||
description: '消息类型(如公告、通知等)',
|
||||
type: 'string',
|
||||
example: 'announcement',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:50')]
|
||||
public string $type;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'title',
|
||||
description: '消息标题',
|
||||
type: 'string',
|
||||
example: '系统升级维护通知',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:100')]
|
||||
public string $title;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'content',
|
||||
description: '消息内容',
|
||||
type: 'string',
|
||||
example: '系统将于2023-12-31 23:00进行维护升级,预计持续2小时',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string')]
|
||||
public string $content;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'sender_id',
|
||||
description: '发送者ID(关联管理员表)',
|
||||
type: 'string',
|
||||
example: '987654321098765432',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string')]
|
||||
public string $sender_id;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'receiver_id',
|
||||
description: '接收者ID(关联管理员表,0表示全员)',
|
||||
type: 'string',
|
||||
example: '567890123456789012',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string')]
|
||||
public string $receiver_id;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'status',
|
||||
description: '消息状态(0=未读,1=已读)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 0,
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|in:0,1')]
|
||||
public int $status;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'priority',
|
||||
description: '优先级(1=低,2=中,3=高)',
|
||||
type: 'integer',
|
||||
example: 3,
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|integer|between:1,3')]
|
||||
public int $priority;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'channel',
|
||||
description: '发送渠道(如system=系统内,email=邮件)',
|
||||
type: 'string',
|
||||
example: 'system',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:20')]
|
||||
public string $channel;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'related_id',
|
||||
description: '关联业务ID(如订单ID、任务ID)',
|
||||
type: 'string',
|
||||
example: '456789012345678901',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:32|nullable')]
|
||||
public ?string $related_id = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'related_type',
|
||||
description: '关联业务类型(如order=订单,task=任务)',
|
||||
type: 'string',
|
||||
example: 'order',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:20|nullable')]
|
||||
public ?string $related_type = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'action_url',
|
||||
description: '操作跳转URL',
|
||||
type: 'string',
|
||||
example: '/system/notice/1',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:255|nullable')]
|
||||
public ?string $action_url = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'action_params',
|
||||
description: '操作参数(JSON字符串)',
|
||||
type: 'string',
|
||||
example: '{"id":1,"type":"detail"}',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|nullable')]
|
||||
public ?string $action_params = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'expired_at',
|
||||
description: '过期时间',
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: '2023-12-31T23:59:59Z',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'date_format:Y-m-d H:i:s|nullable')]
|
||||
public ?string $expired_at = null;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
namespace app\adminapi\schema\request\message;
|
||||
|
||||
|
||||
use app\schema\request\BaseQueryRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '消息列表查询请求',
|
||||
description: '消息列表接口的查询过滤参数'
|
||||
)]
|
||||
class MessageQueryRequest extends BaseQueryRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
property: 'title',
|
||||
description: '消息标题',
|
||||
type: 'string',
|
||||
example: '系统通知',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:100|nullable')]
|
||||
public ?string $title = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'content',
|
||||
description: '消息内容',
|
||||
type: 'string',
|
||||
example: '系统升级',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:255|nullable')]
|
||||
public ?string $content = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'status',
|
||||
description: '消息状态(0=未读,1=已读)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 0,
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'in:0,1|nullable')]
|
||||
public ?int $status = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'priority',
|
||||
description: '优先级(1=低,2=中,3=高)',
|
||||
type: 'integer',
|
||||
enum: [1, 2, 3],
|
||||
example: 3,
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'in:1,2,3|nullable')]
|
||||
public ?int $priority = null;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
namespace app\adminapi\schema\request\notice;
|
||||
|
||||
|
||||
use app\schema\request\BaseFormRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '公告表单',
|
||||
description: '公告创建和编辑接口共用的表单请求参数'
|
||||
)]
|
||||
class NoticeFormRequest extends BaseFormRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
property: 'type',
|
||||
description: '公告类型',
|
||||
type: 'string',
|
||||
enum: ['notice', 'announcement', 'alert'],
|
||||
example: 'notice',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|in:notice,announcement,alert')]
|
||||
public string $type;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'title',
|
||||
description: '公告标题',
|
||||
type: 'string',
|
||||
example: '系统升级通知',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:100')]
|
||||
public string $title;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'content',
|
||||
description: '公告内容',
|
||||
type: 'string',
|
||||
example: '系统将于2025-10-20进行升级维护',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string')]
|
||||
public string $content;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'sort',
|
||||
description: '排序号',
|
||||
type: 'integer',
|
||||
example: 10,
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|integer|min:0|max:1000')]
|
||||
public int $sort;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'enabled',
|
||||
description: '是否启用(1启用 0禁用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1,
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|integer|in:0,1')]
|
||||
public int $enabled;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'remark',
|
||||
description: '备注',
|
||||
type: 'string',
|
||||
example: '重要公告',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:255|nullable')]
|
||||
public ?string $remark = null;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
namespace app\adminapi\schema\request\notice;
|
||||
|
||||
|
||||
use app\schema\request\BaseQueryRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '公告列表查询请求',
|
||||
description: '公告列表接口的查询过滤参数'
|
||||
)]
|
||||
class NoticeQueryRequest extends BaseQueryRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
property: 'title',
|
||||
description: '公告标题',
|
||||
type: 'string',
|
||||
example: '系统公告',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:100|nullable')]
|
||||
public ?string $title = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'type',
|
||||
description: '公告类型',
|
||||
type: 'string',
|
||||
enum: ['notice', 'announcement', 'alert'],
|
||||
example: 'notice',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|in:notice,announcement,alert|nullable')]
|
||||
public ?string $type = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'enabled',
|
||||
description: '是否启用(1启用 0禁用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1,
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'integer|in:0,1|nullable')]
|
||||
public ?int $enabled = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'start_date',
|
||||
description: '创建开始日期',
|
||||
type: 'string',
|
||||
format: 'date',
|
||||
example: '2025-10-01',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'date|nullable')]
|
||||
public ?string $start_date = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'end_date',
|
||||
description: '创建结束日期',
|
||||
type: 'string',
|
||||
format: 'date',
|
||||
example: '2025-10-31',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'date|after_or_equal:start_date|nullable')]
|
||||
public ?string $end_date = null;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\schema\request\org;
|
||||
|
||||
use app\schema\request\BaseFormRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '部门表单',
|
||||
description: '部门创建和编辑接口共用的表单请求参数'
|
||||
)]
|
||||
class DeptFormRequest extends BaseFormRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
description: '部门ID',
|
||||
type: 'string',
|
||||
example: '1',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string')]
|
||||
public string|int|null $id;
|
||||
|
||||
#[OA\Property(
|
||||
description: '父部门ID',
|
||||
type: 'string',
|
||||
example: '0',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string')]
|
||||
public string $parent_id;
|
||||
|
||||
#[OA\Property(
|
||||
description: '部门名称',
|
||||
type: 'string',
|
||||
example: '开发部',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:50|unique:sys_dept,name,' . 'id' . ',id')]
|
||||
public string $name;
|
||||
|
||||
#[OA\Property(
|
||||
description: '部门编码',
|
||||
type: 'string',
|
||||
example: 'dev',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:50|alpha_dash|unique:sys_dept,code,' . 'id' . ',id')]
|
||||
public string $code;
|
||||
|
||||
#[OA\Property(
|
||||
description: '部门领导ID列表',
|
||||
type: 'array',
|
||||
items: new OA\Items(type: 'integer'),
|
||||
example: [1, 2],
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'array|nullable')]
|
||||
public ?array $leader_id_list = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '联系电话',
|
||||
type: 'string',
|
||||
example: '13800138000',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'phone|nullable')]
|
||||
public ?string $phone = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '状态(0:禁用,1:启用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1,
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|in:0,1')]
|
||||
public int $enabled;
|
||||
|
||||
#[OA\Property(
|
||||
description: '排序号',
|
||||
type: 'integer',
|
||||
example: 100,
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|integer|min:0|max:999')]
|
||||
public int $sort;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: https://madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\schema\request\org;
|
||||
|
||||
use app\schema\request\BaseQueryRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '部门列表查询请求',
|
||||
description: '部门列表接口的查询过滤参数'
|
||||
)]
|
||||
class DeptQueryRequest extends BaseQueryRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
description: '部门编码',
|
||||
type: 'string',
|
||||
example: 'dev',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:50|nullable')]
|
||||
public ?string $code = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '部门名称',
|
||||
type: 'string',
|
||||
example: '开发部',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:50|nullable')]
|
||||
public ?string $name = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '状态(0:禁用,1:启用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1,
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'in:0,1|nullable')]
|
||||
public ?int $enabled = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '排序字段',
|
||||
type: 'string',
|
||||
default: 'sort asc',
|
||||
example: 'sort asc'
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|nullable')]
|
||||
public ?string $order = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '返回数据格式(normal|tree|select)',
|
||||
type: 'string',
|
||||
default: 'normal',
|
||||
enum: ['normal', 'tree', 'select'],
|
||||
example: 'normal'
|
||||
)]
|
||||
#[ValidationRules(rules: 'in:normal,tree,select|nullable')]
|
||||
public ?string $format = null;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
namespace app\adminapi\schema\request\org;
|
||||
|
||||
use app\schema\request\BaseFormRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '岗位表单',
|
||||
description: '岗位创建和编辑接口共用的表单请求参数'
|
||||
)]
|
||||
class PostFormRequest extends BaseFormRequest
|
||||
{
|
||||
|
||||
|
||||
#[OA\Property(
|
||||
property: 'dept_id',
|
||||
description: '部门ID',
|
||||
type: 'string',
|
||||
example: '246996721795072000',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string')]
|
||||
public string $dept_id;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'code',
|
||||
description: '岗位编码',
|
||||
type: 'string',
|
||||
example: 'SDF',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:30|unique:sys_post,code')]
|
||||
public string $code;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'name',
|
||||
description: '岗位名称',
|
||||
type: 'string',
|
||||
example: 'AD',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:50')]
|
||||
public string $name;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'sort',
|
||||
description: '排序号',
|
||||
type: 'integer',
|
||||
example: 0,
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|integer|min:0|max:1000')]
|
||||
public int $sort;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'enabled',
|
||||
description: '状态(1启用 0禁用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1,
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|integer|in:0,1')]
|
||||
public int $enabled;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'remark',
|
||||
description: '备注',
|
||||
type: 'string',
|
||||
example: 'ASDF',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:255|nullable')]
|
||||
public ?string $remark = null;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
namespace app\adminapi\schema\request\org;
|
||||
|
||||
|
||||
|
||||
use app\schema\request\BaseQueryRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '岗位列表查询请求',
|
||||
description: '岗位列表接口的查询过滤参数'
|
||||
)]
|
||||
class PostQueryRequest extends BaseQueryRequest
|
||||
{
|
||||
|
||||
|
||||
#[OA\Property(
|
||||
property: 'name',
|
||||
description: '岗位名称',
|
||||
type: 'string',
|
||||
example: '技术主管',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:50|nullable')]
|
||||
public ?string $name = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'code',
|
||||
description: '岗位编码',
|
||||
type: 'string',
|
||||
example: 'TECH_LEAD',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|max:30|nullable')]
|
||||
public ?string $code = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'dept_id',
|
||||
description: '部门ID',
|
||||
type: 'string',
|
||||
example: '246996721795072000',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'string|nullable')]
|
||||
public ?string $dept_id = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'enabled',
|
||||
description: '状态(1启用 0禁用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1,
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'integer|in:0,1|nullable')]
|
||||
public ?int $enabled = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'start_date',
|
||||
description: '创建开始日期',
|
||||
type: 'string',
|
||||
format: 'date',
|
||||
example: '2025-11-01',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'date|nullable')]
|
||||
public ?string $start_date = null;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'end_date',
|
||||
description: '创建结束日期',
|
||||
type: 'string',
|
||||
format: 'date',
|
||||
example: '2025-11-30',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'date|after_or_equal:start_date|nullable')]
|
||||
public ?string $end_date = null;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\schema\request\plugin;
|
||||
|
||||
use app\schema\request\BaseFormRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '应用授权表单',
|
||||
description: '应用授权创建和编辑接口共用的表单请求参数'
|
||||
)]
|
||||
class ApplicationAuthFormRequest extends BaseFormRequest
|
||||
{
|
||||
#[OA\Property(property: 'name', description: '应用名称', type: 'string', example: '开放平台应用', nullable: false)]
|
||||
#[ValidationRules(rules: 'required|string|max:100')]
|
||||
public string $name;
|
||||
|
||||
#[OA\Property(property: 'expired_at', description: '失效时间戳', type: 'integer', example: 1795104000, nullable: true)]
|
||||
#[ValidationRules(rules: 'nullable|integer')]
|
||||
public ?int $expired_at = null;
|
||||
|
||||
#[OA\Property(property: 'enabled', description: '状态(1正常 0停用)', type: 'integer', enum: [0, 1], example: 1, nullable: false)]
|
||||
#[ValidationRules(rules: 'required|integer|in:0,1')]
|
||||
public int $enabled;
|
||||
|
||||
#[OA\Property(property: 'description', description: '描述', type: 'string', example: '用于第三方应用接口调用', nullable: true)]
|
||||
#[ValidationRules(rules: 'nullable|string|max:500')]
|
||||
public ?string $description = null;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\schema\request\plugin;
|
||||
|
||||
use app\schema\request\BaseQueryRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '应用授权查询',
|
||||
description: '应用授权列表查询请求参数'
|
||||
)]
|
||||
class ApplicationAuthQueryRequest extends BaseQueryRequest
|
||||
{
|
||||
#[OA\Property(property: 'LIKE_name', description: '应用名称', type: 'string', example: '开放平台', nullable: true)]
|
||||
#[ValidationRules(rules: 'nullable|string|max:100')]
|
||||
public ?string $LIKE_name = null;
|
||||
|
||||
#[OA\Property(property: 'LIKE_access_key', description: 'Access Key', type: 'string', example: 'AK', nullable: true)]
|
||||
#[ValidationRules(rules: 'nullable|string|max:64')]
|
||||
public ?string $LIKE_access_key = null;
|
||||
|
||||
#[OA\Property(property: 'EQ_enabled', description: '状态', type: 'integer', enum: [0, 1], example: 1, nullable: true)]
|
||||
#[ValidationRules(rules: 'nullable|integer|in:0,1')]
|
||||
public ?int $EQ_enabled = null;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\schema\request\profile;
|
||||
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '头像更新请求',
|
||||
description: '更新当前登录用户的头像'
|
||||
)]
|
||||
final class AvatarUpdateRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
property: 'avatar',
|
||||
description: '头像 URL 或 Base64 编码',
|
||||
type: 'string',
|
||||
example: '/upload/avatar.jpg'
|
||||
)]
|
||||
public string $avatar;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\schema\request\profile;
|
||||
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '密码修改请求',
|
||||
description: '修改当前登录用户的密码'
|
||||
)]
|
||||
final class PasswordUpdateRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
property: 'old_password',
|
||||
description: '当前密码',
|
||||
type: 'string',
|
||||
minLength: 6,
|
||||
maxLength: 20,
|
||||
example: 'old123456'
|
||||
)]
|
||||
public string $old_password;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'new_password',
|
||||
description: '新密码',
|
||||
type: 'string',
|
||||
minLength: 6,
|
||||
maxLength: 20,
|
||||
example: 'new123456'
|
||||
)]
|
||||
public string $new_password;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'confirm_password',
|
||||
description: '确认新密码',
|
||||
type: 'string',
|
||||
minLength: 6,
|
||||
maxLength: 20,
|
||||
example: 'new123456'
|
||||
)]
|
||||
public string $confirm_password;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\schema\request\profile;
|
||||
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '个人信息更新请求',
|
||||
description: '更新当前登录用户的个人信息'
|
||||
)]
|
||||
final class ProfileUpdateRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
property: 'real_name',
|
||||
description: '真实姓名',
|
||||
type: 'string',
|
||||
minLength: 2,
|
||||
maxLength: 50,
|
||||
example: '张三'
|
||||
)]
|
||||
public string $real_name;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'nick_name',
|
||||
description: '昵称',
|
||||
type: 'string',
|
||||
minLength: 2,
|
||||
maxLength: 30,
|
||||
example: '小明'
|
||||
)]
|
||||
public string $nick_name;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'email',
|
||||
description: '邮箱',
|
||||
type: 'string',
|
||||
format: 'email',
|
||||
example: 'user@example.com'
|
||||
)]
|
||||
public string $email;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'mobile_phone',
|
||||
description: '手机号码',
|
||||
type: 'string',
|
||||
pattern: '^1[3-9]\\d{9}$',
|
||||
example: '13800138000'
|
||||
)]
|
||||
public string $mobile_phone;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'sex',
|
||||
description: '性别:0=未知,1=男,2=女',
|
||||
type: 'integer',
|
||||
enum: [0, 1, 2],
|
||||
example: 1
|
||||
)]
|
||||
public int $sex;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'signed',
|
||||
description: '个人签名',
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
example: '专注于用户体验设计'
|
||||
)]
|
||||
public ?string $signed;
|
||||
|
||||
#[OA\Property(
|
||||
property: 'address',
|
||||
description: '地址',
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
example: '北京市朝阳区'
|
||||
)]
|
||||
public ?string $address;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: https://madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\schema\request\system;
|
||||
|
||||
|
||||
use app\schema\request\BaseFormRequest;
|
||||
use OpenApi\Attributes as OA;
|
||||
use WebmanTech\DTO\Attributes\ValidationRules;
|
||||
|
||||
#[OA\Schema(
|
||||
title: '管理员表单',
|
||||
description: '管理员创建和编辑接口共用的表单请求参数'
|
||||
)]
|
||||
class AdminFormRequest extends BaseFormRequest
|
||||
{
|
||||
#[OA\Property(
|
||||
description: '用户名',
|
||||
type: 'string',
|
||||
example: 'admin',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:50|unique:sys_admin,user_name')]
|
||||
public string $user_name;
|
||||
|
||||
#[OA\Property(
|
||||
description: '真实姓名',
|
||||
type: 'string',
|
||||
example: '系统管理员',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|max:50')]
|
||||
public string $real_name;
|
||||
|
||||
#[OA\Property(
|
||||
description: '密码',
|
||||
type: 'string',
|
||||
example: '123456',
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|string|min:6|max:20')]
|
||||
public string $password;
|
||||
|
||||
#[OA\Property(
|
||||
description: '手机号码',
|
||||
type: 'string',
|
||||
example: '18888888888',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'mobile|nullable')]
|
||||
public ?string $mobile_phone = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '邮箱',
|
||||
type: 'string',
|
||||
example: 'admin@example.com',
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'email|nullable')]
|
||||
public ?string $email = null;
|
||||
|
||||
#[OA\Property(
|
||||
description: '状态(0:禁用,1:启用)',
|
||||
type: 'integer',
|
||||
enum: [0, 1],
|
||||
example: 1,
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|in:0,1')]
|
||||
public int $status;
|
||||
|
||||
#[OA\Property(
|
||||
description: '部门ID',
|
||||
type: 'integer',
|
||||
example: 1,
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|integer|min:1')]
|
||||
public int $dept_id;
|
||||
|
||||
#[OA\Property(
|
||||
description: '角色ID列表',
|
||||
type: 'array',
|
||||
items: new OA\Items(type: 'integer'),
|
||||
example: [1, 2],
|
||||
nullable: false
|
||||
)]
|
||||
#[ValidationRules(rules: 'required|array|min:1')]
|
||||
public array $role_id_list;
|
||||
|
||||
#[OA\Property(
|
||||
description: '岗位ID列表',
|
||||
type: 'array',
|
||||
items: new OA\Items(type: 'integer'),
|
||||
example: [1],
|
||||
nullable: true
|
||||
)]
|
||||
#[ValidationRules(rules: 'array|nullable')]
|
||||
public ?array $post_id_list = null;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user