初始化本地仓库

This commit is contained in:
2026-08-31 11:33:59 +08:00
commit 663f74b633
2390 changed files with 402918 additions and 0 deletions
+35
View File
@@ -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());
}
}
}