no message
This commit is contained in:
parent
0d50597905
commit
815163b8e4
@ -9,6 +9,7 @@ use App\Middleware\GemboxToken;
|
|||||||
use App\Middleware\TgHookAdmin;
|
use App\Middleware\TgHookAdmin;
|
||||||
use App\Middleware\TgAdmin;
|
use App\Middleware\TgAdmin;
|
||||||
use App\Middleware\User;
|
use App\Middleware\User;
|
||||||
|
use App\Middleware\VerifyNasaToken;
|
||||||
use Slim\Routing\RouteCollectorProxy;
|
use Slim\Routing\RouteCollectorProxy;
|
||||||
|
|
||||||
return static function (Slim\App $app): void {
|
return static function (Slim\App $app): void {
|
||||||
@ -441,4 +442,52 @@ return static function (Slim\App $app): void {
|
|||||||
// $group->post('/user/online_ip', App\Controllers\Api\NodeApiV1Controller::class . ':addUserOnlineIp');
|
// $group->post('/user/online_ip', App\Controllers\Api\NodeApiV1Controller::class . ':addUserOnlineIp');
|
||||||
// $group->post('/user/detect_log', App\Controllers\Api\NodeApiV1Controller::class . ':addUserDetectLog');
|
// $group->post('/user/detect_log', App\Controllers\Api\NodeApiV1Controller::class . ':addUserDetectLog');
|
||||||
//})->add(new NodeApi());
|
//})->add(new NodeApi());
|
||||||
|
|
||||||
|
$app->group('/api/nasa/v1', function (RouteCollectorProxy $group): void {
|
||||||
|
$group->get('/node', App\Controllers\Api\Nasa\V1\NodeController::class . ':index');
|
||||||
|
$group->get('/node/detail/{id}', App\Controllers\Api\Nasa\V1\NodeController::class . ':detail');
|
||||||
|
$group->put('/node/detail/{id}', App\Controllers\Api\Nasa\V1\NodeController::class . ':detailSave');
|
||||||
|
$group->delete('/node/detail/{id}', App\Controllers\Api\Nasa\V1\NodeController::class . ':delete');
|
||||||
|
$group->get('/node/add', App\Controllers\Api\Nasa\V1\NodeController::class . ':add');
|
||||||
|
$group->put('/node/add', App\Controllers\Api\Nasa\V1\NodeController::class . ':addSave');
|
||||||
|
$group->get('/node/clone/{id}', App\Controllers\Api\Nasa\V1\NodeController::class . ':clone');
|
||||||
|
$group->put('/node/clone/', App\Controllers\Api\Nasa\V1\NodeController::class . ':cloneSave');
|
||||||
|
$group->get('/node/hit/{id}/{field}', App\Controllers\Api\Nasa\V1\NodeController::class . ':hit');
|
||||||
|
})->add(new VerifyNasaToken());
|
||||||
|
|
||||||
|
|
||||||
|
// Route::prefix('setting')->group(function() {
|
||||||
|
// Route::get('nav', [NasaV1SettingNavController::class, 'index']);
|
||||||
|
// Route::get('nav/detail/{id}', [NasaV1SettingNavController::class, 'detail']);
|
||||||
|
// Route::put('nav/detail/{id}', [NasaV1SettingNavController::class, 'detailSave']);
|
||||||
|
// Route::delete('nav/delete/{id}', [NasaV1SettingNavController::class, 'delete']);
|
||||||
|
// Route::get('nav/add', [NasaV1SettingNavController::class, 'add']);
|
||||||
|
// Route::put('nav/add', [NasaV1SettingNavController::class, 'addSave']);
|
||||||
|
// Route::get('nav/clone/{id}', [NasaV1SettingNavController::class, 'clone']);
|
||||||
|
// Route::put('nav/clone', [NasaV1SettingNavController::class, 'cloneSave']);
|
||||||
|
// Route::get('nav/hit/{id}/{field}', [NasaV1SettingNavController::class, 'hit']);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// $app->group('/api/nasa/v1', function (RouteCollectorProxy $group): void {
|
||||||
|
// $group->get('/node', App\Controllers\Nasa\V1\NodeController::class . ':index');
|
||||||
|
// })->add(new VerifyNasaToken());
|
||||||
|
|
||||||
|
// Route::prefix('nasa/v1')->group(function () {
|
||||||
|
//
|
||||||
|
// Route::prefix('node')->group(function() {
|
||||||
|
// Route::get('/', [App\Controllers\Nasa\V1\NodeController::class . ':index');
|
||||||
|
// Route::get('/detail/{id}', [NasaV1SettingNavController::class, 'detail']);
|
||||||
|
// Route::put('/detail/{id}', [NasaV1SettingNavController::class, 'detailSave']);
|
||||||
|
// Route::delete('/delete/{id}', [NasaV1SettingNavController::class, 'delete']);
|
||||||
|
// Route::get('/add', [NasaV1SettingNavController::class, 'add']);
|
||||||
|
// Route::put('/add', [NasaV1SettingNavController::class, 'addSave']);
|
||||||
|
// Route::get('/clone/{id}', [NasaV1SettingNavController::class, 'clone']);
|
||||||
|
// Route::put('/clone', [NasaV1SettingNavController::class, 'cloneSave']);
|
||||||
|
// Route::get('/hit/{id}/{field}', [NasaV1SettingNavController::class, 'hit']);
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// })->add(new VerifyNasaToken());
|
||||||
|
|
||||||
|
|
||||||
|
// $app->get('/api/huodong/findbydate/{date}', App\Controllers\Nasa\V1\NodeController::class . ':index');
|
||||||
};
|
};
|
||||||
|
|||||||
@ -21,13 +21,26 @@ use GuzzleHttp\Psr7\ServerRequest;
|
|||||||
use Slim\Factory\AppFactory;
|
use Slim\Factory\AppFactory;
|
||||||
use Slim\Http\Factory\DecoratedResponseFactory;
|
use Slim\Http\Factory\DecoratedResponseFactory;
|
||||||
|
|
||||||
function tomd($d, $type = 0)
|
function t($x)
|
||||||
{
|
{
|
||||||
echo "<pre>";
|
tomd($x);
|
||||||
var_dump($d);
|
}
|
||||||
|
|
||||||
if ($type) {
|
function tt($x)
|
||||||
exit;
|
{
|
||||||
|
tomd($x, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tomd($aData, $iType = 0) {
|
||||||
|
|
||||||
|
if ($_ENV["is_loc"] == true) {
|
||||||
|
echo "<pre>";
|
||||||
|
var_dump($aData);
|
||||||
|
|
||||||
|
if ($iType == 1) {
|
||||||
|
http_response_code(500);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
464
src/Controllers/Api/Nasa/V1/Base/ListController.php
Executable file
464
src/Controllers/Api/Nasa/V1/Base/ListController.php
Executable file
@ -0,0 +1,464 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Controllers\Api\Nasa\V1\Base;
|
||||||
|
|
||||||
|
//use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Slim\Http\Response;
|
||||||
|
//use Slim\Http\ServerRequest;
|
||||||
|
use Slim\Http\ServerRequest as Request;
|
||||||
|
|
||||||
|
abstract class ListController
|
||||||
|
{
|
||||||
|
private string $sOperatorDefault = 'eq';
|
||||||
|
private string $sSortDefault = 'asc';
|
||||||
|
|
||||||
|
private array $aFilterParams = [
|
||||||
|
'c' => [],
|
||||||
|
'cc' => [],
|
||||||
|
'ob' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $oModel;
|
||||||
|
protected string $sPageName = '';
|
||||||
|
protected string $sModelName = '';
|
||||||
|
protected array $aFilterFields = []; // 允许where的字段
|
||||||
|
// protected array $aArticleFields = []; // 文章字段
|
||||||
|
protected array $aFieldTypes = [];
|
||||||
|
protected array $aLenthFields = []; // 限制长度的字段
|
||||||
|
protected array $aSelectFields = []; // 默认作为select的字段
|
||||||
|
protected array $aListFields = []; // list展示的字段和名字映射
|
||||||
|
protected array $aDateFields = []; // date字段
|
||||||
|
protected array $aJsonFields = []; // json字段
|
||||||
|
protected array $aValueStyle = []; // 特定文字渲染样式
|
||||||
|
protected int $iLimitMax = 100; // 默认limit限制
|
||||||
|
protected int $iLimit = 20; // 默认显示行数
|
||||||
|
protected array $aCcExtFields = []; // 自定义条件字段追加在listFields后
|
||||||
|
protected array $aCcFields = [];
|
||||||
|
protected int $iOrderByMax = 3;
|
||||||
|
protected array $aOrderBy = [
|
||||||
|
[
|
||||||
|
"sField" => "id",
|
||||||
|
"sSort" => "desc"
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if ($this->sModelName) {
|
||||||
|
$sFullNamespace = "App\Models\\{$this->sModelName}";
|
||||||
|
if (! class_exists($sFullNamespace)) {
|
||||||
|
throw new InvalidArgumentException("【系统架构错误】: 找不到领域模型 {$sFullNamespace}");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->oModel = new $sFullNamespace();
|
||||||
|
$this->aCcFields = array_merge($this->aListFields, $this->aCcExtFields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index(Request $oRequest, Response $oResponse, $args)
|
||||||
|
{
|
||||||
|
if (! $this->oModel) {
|
||||||
|
return $oResponse->withJson(['code' => 500, 'msg' => '未绑定核心领域模型'], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oQuery = $this->oModel->newQuery();
|
||||||
|
|
||||||
|
$aParams = $oRequest->getParams();
|
||||||
|
|
||||||
|
foreach ($aParams as $k => $v) {
|
||||||
|
|
||||||
|
if (!empty($this->aFilterFields)) {
|
||||||
|
if (!in_array($k, $this->aFilterFields)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// w__name__eq=1
|
||||||
|
$aK = explode("__", $k);
|
||||||
|
$sType = $aK[0] ?? '';
|
||||||
|
$sField = $aK[1] ?? '';
|
||||||
|
$sOperator = $aK[2] ?? $this->sOperatorDefault;
|
||||||
|
$sSqlOperator = $this->mapOperator($sOperator);
|
||||||
|
|
||||||
|
if (!isset($this->aFilterParams[$sType])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$sType || !$sField) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->aFilterParams[$k] = $v;
|
||||||
|
|
||||||
|
if ($sType === 'ob') {
|
||||||
|
$this->aFilterParams[$sType][] = [
|
||||||
|
'sField' => $sField,
|
||||||
|
'sSort' => $v ?? $this->sSortDefault
|
||||||
|
];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sType === 'c') { // 预制条件,字典式
|
||||||
|
$this->aFilterParams[$sType][$sField] = [
|
||||||
|
'sOperator' => $sOperator,
|
||||||
|
'sValue' => $v
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sType === 'cc') { // 自定义条件,集合式
|
||||||
|
$this->aFilterParams[$sType][] = [
|
||||||
|
'sField' => $sField,
|
||||||
|
'sOperator' => $sOperator,
|
||||||
|
'sValue' => $v
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blank($v)) continue; // 空v可以back但不加入sql查询
|
||||||
|
|
||||||
|
$sSqlValue = $v;
|
||||||
|
if ($sSqlOperator === 'like') {
|
||||||
|
$sSqlValue = "%{$v}%";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($sField, $this->aDateFields)) {
|
||||||
|
$oQuery->whereDate($sField, $sSqlOperator, $sSqlValue);
|
||||||
|
} else {
|
||||||
|
$oQuery->where($sField, $sSqlOperator, $sSqlValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$iLimit = (int) $oRequest->getParam('limit', $this->iLimit);
|
||||||
|
$iLimit = max(1, $iLimit);
|
||||||
|
$iLimit = min($this->iLimitMax, $iLimit);
|
||||||
|
|
||||||
|
if (!empty($this->aFilterParams['ob'])) {
|
||||||
|
$this->aOrderBy = $this->aFilterParams['ob'];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->aOrderBy as $aOrderBy) {
|
||||||
|
$oQuery->orderBy($aOrderBy['sField'], $aOrderBy['sSort']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oPaginatedData = $oQuery->paginate($iLimit, array_keys($this->aListFields));
|
||||||
|
|
||||||
|
return $oResponse->withJson([
|
||||||
|
'iCode' => 200,
|
||||||
|
'aData' => [
|
||||||
|
'cList' => $oPaginatedData->items(),
|
||||||
|
'aListField' => $this->getListFields(),
|
||||||
|
'aFieldTypes' => $this->aFieldTypes,
|
||||||
|
'aMap' => $this->oModel->getMapAll(),
|
||||||
|
'aValueStyle' => $this->aValueStyle,
|
||||||
|
'aSelectField' => $this->aSelectFields,
|
||||||
|
'aFilterParam' => $this->aFilterParams,
|
||||||
|
'aCcField' => $this->aCcFields,
|
||||||
|
'sCcLast' => $this->getCcLast(),
|
||||||
|
'sPageName' => $this->sPageName,
|
||||||
|
'aPager' => [
|
||||||
|
'aOrderBySchema'=> $this->getOrderBySchema(),
|
||||||
|
'aOrderBy' => $this->getOrderBy(),
|
||||||
|
'iTotal' => $oPaginatedData->total(),
|
||||||
|
'iLimit' => min($this->iLimitMax, $iLimit),
|
||||||
|
'iCurrentPage' => $oPaginatedData->currentPage(),
|
||||||
|
'iLastPage' => $oPaginatedData->lastPage(),
|
||||||
|
'iPerPage' => $oPaginatedData->perPage(),
|
||||||
|
'iPrevPage' => max(1, $oPaginatedData->currentPage() - 1),
|
||||||
|
'iNextPage' => min($oPaginatedData->lastPage(), $oPaginatedData->currentPage() + 1)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], 200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mapOperator($sOperator)
|
||||||
|
{
|
||||||
|
$sSqlOperator = match ($sOperator) {
|
||||||
|
'like' => 'like',
|
||||||
|
'eq' => '=',
|
||||||
|
'ne' => '!=',
|
||||||
|
'gt' => '>',
|
||||||
|
'gte' => '>=',
|
||||||
|
'lt' => '<',
|
||||||
|
'lte' => '<=',
|
||||||
|
default => '',
|
||||||
|
};
|
||||||
|
|
||||||
|
return $sSqlOperator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getOrderBySchema()
|
||||||
|
{
|
||||||
|
$iOrderByCount = count($this->aOrderBy);
|
||||||
|
$iDiff = $this->iOrderByMax - $iOrderByCount;
|
||||||
|
$a = $this->aOrderBy;
|
||||||
|
for ($i=0; $i<$iDiff; $i++) {
|
||||||
|
$a[] = [];
|
||||||
|
}
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getOrderBy()
|
||||||
|
{
|
||||||
|
return $this->aOrderBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getListFields()
|
||||||
|
{
|
||||||
|
$a = [];
|
||||||
|
|
||||||
|
foreach ($this->aListFields as $k => $v) {
|
||||||
|
if ($v) {
|
||||||
|
$a[$k] = $v;
|
||||||
|
} else {
|
||||||
|
$a[$k] = $k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getCcLast()
|
||||||
|
{
|
||||||
|
$aCcFieldsTmp = $this->aCcFields;
|
||||||
|
|
||||||
|
foreach ($this->aFilterParams['cc'] as $ACC) {
|
||||||
|
unset($aCcFieldsTmp[$ACC['sField']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$s = head(array_keys($aCcFieldsTmp));
|
||||||
|
|
||||||
|
return $s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add(Request $oRequest, Response $oResponse, $args): \Illuminate\Http\JsonResponse
|
||||||
|
{
|
||||||
|
$sTableName = $this->oModel->getTable();
|
||||||
|
$aRawFields = Schema::getColumnListing($sTableName);
|
||||||
|
|
||||||
|
$sClassName = get_class($this->oModel);
|
||||||
|
|
||||||
|
$aKickFields = [];
|
||||||
|
|
||||||
|
if (defined($sClassName . '::UPDATED_AT')) {
|
||||||
|
$aKickFields[] = constant($sClassName . '::UPDATED_AT');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defined($sClassName . '::CREATED_AT')) {
|
||||||
|
$aKickFields[] = constant($sClassName . '::CREATED_AT');
|
||||||
|
}
|
||||||
|
|
||||||
|
$aKickFields[] = "id";
|
||||||
|
|
||||||
|
$aRawFieldsClear = [];
|
||||||
|
foreach ($aRawFields as $aRawField) {
|
||||||
|
if (in_array($aRawField, $aKickFields)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$aRawFieldsClear[] = $aRawField;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oResponse->withJson([
|
||||||
|
'iCode' => 200,
|
||||||
|
'aData' => [
|
||||||
|
'cList' => $aRawFieldsClear,
|
||||||
|
'sPageName' => $this->sPageName
|
||||||
|
]
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clone(Request $oRequest, Response $oResponse, $args)
|
||||||
|
{
|
||||||
|
$iId = $args['id'];
|
||||||
|
$oModelResult = $this->oModel->findOrFail($iId);
|
||||||
|
$cList = $oModelResult->getRawOriginal();
|
||||||
|
|
||||||
|
$sTableName = $this->oModel->getTable();
|
||||||
|
$aStatus = DB::select("SHOW TABLE STATUS LIKE '{$sTableName}'");
|
||||||
|
$iNextId = $aStatus[0]->Auto_increment ?? 1;
|
||||||
|
|
||||||
|
$cList['id'] = $iNextId;
|
||||||
|
|
||||||
|
return $oResponse->withJson([
|
||||||
|
'iCode' => 200,
|
||||||
|
'aData' => [
|
||||||
|
'cList' => $cList,
|
||||||
|
'aMap' => $this->oModel->getMapAll(),
|
||||||
|
'aListField' => $this->getListFields(),
|
||||||
|
'sPageName' => $this->sPageName
|
||||||
|
]
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cloneSave(Request $oRequest, Response $oResponse, $args)
|
||||||
|
{
|
||||||
|
$iId = $args['id'];
|
||||||
|
|
||||||
|
$aParams = $oRequest->getParams();
|
||||||
|
|
||||||
|
foreach ($aParams as $k => $v) {
|
||||||
|
$this->oModal->{$k} = $v;
|
||||||
|
}
|
||||||
|
$this->oModel->create();
|
||||||
|
|
||||||
|
return $oResponse->withJson([
|
||||||
|
'iCode' => 200,
|
||||||
|
'aData' => [],
|
||||||
|
'sMsg' => "保存成功:id ".$this->oModel->id
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function detail(Request $oRequest, Response $oResponse, $args)
|
||||||
|
{
|
||||||
|
$iId = $args['id'];
|
||||||
|
$oModelResult = $this->oModel->findOrFail($iId);
|
||||||
|
$cList = $oModelResult->getRawOriginal();
|
||||||
|
|
||||||
|
return $oResponse->withJson([
|
||||||
|
'iCode' => 200,
|
||||||
|
'aData' => [
|
||||||
|
'cList' => $cList,
|
||||||
|
'aMap' => $this->oModel->getMapAll(),
|
||||||
|
'aListField' => $this->getListFields(),
|
||||||
|
'sPageName' => $this->sPageName
|
||||||
|
]
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hit(Request $oRequest, Response $oResponse, $args)
|
||||||
|
{
|
||||||
|
$iId = $args['id'];
|
||||||
|
$sField = $args['field'];
|
||||||
|
$sHitType = $this->aFieldTypes[$sField] ?? '';
|
||||||
|
|
||||||
|
$sHit = $this->oModel->find($iId)?->{$sField};
|
||||||
|
|
||||||
|
if ($sHitType) {
|
||||||
|
if ($sHitType == 'json') {
|
||||||
|
$aHit = json_decode($sHit);
|
||||||
|
$sHit = json_encode($aHit, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oResponse->withJson([
|
||||||
|
'iCode' => 200,
|
||||||
|
'aData' => [
|
||||||
|
'sHit' => $sHit
|
||||||
|
]
|
||||||
|
], 200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(Request $oRequest, Response $oResponse, $args)
|
||||||
|
{
|
||||||
|
$iId = $args['id'];
|
||||||
|
|
||||||
|
$this->oModel->findOrFail($iId)->delete();
|
||||||
|
|
||||||
|
return $oResponse->withJson([
|
||||||
|
'iCode' => 200,
|
||||||
|
'aData' => [],
|
||||||
|
'sMsg' => '删除成功'
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addSave(Request $oRequest, Response $oResponse, $args)
|
||||||
|
{
|
||||||
|
$aParams = $oRequest->getParams();
|
||||||
|
|
||||||
|
foreach ($aParams as $k => $v) {
|
||||||
|
$this->oModel->{$k} = $v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->oModel->save()) {
|
||||||
|
return $oResponse->withJson(['code' => 500, 'msg' => '保存失败?'], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oResponse->withJson([
|
||||||
|
'iCode' => 200,
|
||||||
|
'aData' => [],
|
||||||
|
'sMsg' => '保存成功'
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function detailSave(Request $oRequest, Response $oResponse, $args)
|
||||||
|
{
|
||||||
|
$iId = $args['id'];
|
||||||
|
|
||||||
|
$oThisModel = $this->oModel->find($iId);
|
||||||
|
|
||||||
|
$aParams = $oRequest->getParams();
|
||||||
|
|
||||||
|
unset($aParams['id'], $aParams['_method']);
|
||||||
|
|
||||||
|
foreach ($aParams as $k => &$v) {
|
||||||
|
if (empty($v) && empty($oThisModel->{$k})) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (empty($v) && $v !== 0 && $v !== '0') {
|
||||||
|
$v = '';
|
||||||
|
}
|
||||||
|
if ($oThisModel->{$k} != $v) {
|
||||||
|
$oThisModel->{$k} = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$oThisModel->save();
|
||||||
|
|
||||||
|
$aChangeField = $oThisModel->getChanges();
|
||||||
|
|
||||||
|
/// 排除update_at
|
||||||
|
$sClassName = get_class($this->oModel);
|
||||||
|
if (
|
||||||
|
defined($sClassName . '::UPDATED_AT')
|
||||||
|
&& ($sUpdateColumn = constant($sClassName . '::UPDATED_AT'))
|
||||||
|
&& !isset($aParams[$sUpdateColumn])
|
||||||
|
&& isset($aChangeField[$sUpdateColumn])
|
||||||
|
) {
|
||||||
|
unset($aChangeField[$sUpdateColumn]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 构造msg
|
||||||
|
$sMsg = "";
|
||||||
|
$sJiantou = " -> ";
|
||||||
|
foreach ($aChangeField as $k => $v) {
|
||||||
|
|
||||||
|
if (mb_strlen($v, 'UTF-8') > 60 ) {
|
||||||
|
$sJiantou = '';
|
||||||
|
// $v = mb_substr($v, 0, 60, 'UTF-8');
|
||||||
|
$v = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($aParams[$k])) {
|
||||||
|
$sMsg .= "⚠️ ".$k . $sJiantou . $v . "<br><br>";
|
||||||
|
} else {
|
||||||
|
$sMsg .= "✅ ".$k . $sJiantou . $v . "<br><br>";
|
||||||
|
unset($aParams[$k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($aParams as $k => $v) {
|
||||||
|
|
||||||
|
if (mb_strlen($v, 'UTF-8') > 60 ) {
|
||||||
|
$sJiantou = '';
|
||||||
|
// $v = mb_substr($v, 0, 60, 'UTF-8');
|
||||||
|
$v = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$sMsg .= "❌ ".$k . $sJiantou . $v . "<br><br>";
|
||||||
|
}
|
||||||
|
$sMsg = rtrim($sMsg, "<br><br>");
|
||||||
|
|
||||||
|
return $oResponse->withJson([
|
||||||
|
'iCode' => 200,
|
||||||
|
'aData' => $aChangeField,
|
||||||
|
'sMsg' => $sMsg
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
129
src/Controllers/Api/Nasa/V1/NodeController.php
Executable file
129
src/Controllers/Api/Nasa/V1/NodeController.php
Executable file
@ -0,0 +1,129 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Controllers\Api\Nasa\V1;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Controllers\Api\Nasa\V1\Base\ListController;
|
||||||
|
|
||||||
|
final class NodeController extends ListController
|
||||||
|
{
|
||||||
|
protected string $sModelName = 'node';
|
||||||
|
protected array $aFilterFields = [ // 允许where的字段,不设视为全允许
|
||||||
|
// 's_name',
|
||||||
|
// 'i_status'
|
||||||
|
];
|
||||||
|
|
||||||
|
protected string $sPageName = "node";
|
||||||
|
|
||||||
|
protected array $aListFields = [ // list展示的字段和名字映射
|
||||||
|
'id' => 'id',
|
||||||
|
'name' => 'name',
|
||||||
|
'type' => '启用',
|
||||||
|
'server' => 'server',
|
||||||
|
'custom_config' => 'custom_config',
|
||||||
|
'sort' => 'sort',
|
||||||
|
'traffic_rate' => '倍率',
|
||||||
|
'node_class' => 'node_class',
|
||||||
|
'node_speedlimit' => '限速',
|
||||||
|
'node_bandwidth' => 'node_bandwidth',
|
||||||
|
'node_bandwidth_limit' => 'node_bandwidth_limit',
|
||||||
|
'bandwidthlimit_resetday'=> 'bandwidthlimit_resetday',
|
||||||
|
'node_heartbeat' => 'node_heartbeat',
|
||||||
|
'online_user' => 'online_user',
|
||||||
|
'ipv4' => 'ipv4',
|
||||||
|
'ipv6' => 'ipv6',
|
||||||
|
'node_group' => 'node_group',
|
||||||
|
'online' => 'online',
|
||||||
|
'gfw_block' => 'gfw_block',
|
||||||
|
'order_group' => 'order_group',
|
||||||
|
'zhuanfa_code' => 'zhuanfa_code',
|
||||||
|
'duck_code' => 'duck_code',
|
||||||
|
'duck_group' => 'duck_group',
|
||||||
|
'memo' => 'memo',
|
||||||
|
'is_shadow' => 'is_shadow',
|
||||||
|
'traffic_rate_front' => 'traffic_rate_front',
|
||||||
|
'memo_front' => 'memo_front',
|
||||||
|
'fly' => 'fly',
|
||||||
|
'pid' => 'pid',
|
||||||
|
'prev_day_width' => 'prev_day_width',
|
||||||
|
'country_code' => 'country_code',
|
||||||
|
'country_emoji' => 'country_emoji',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected array $aValueStyle = [ // 特定文字渲染样式
|
||||||
|
'关闭' => "<span class='text-danger'>关闭</span>",
|
||||||
|
'开启' => "<span class='text-success'>开启</span>"
|
||||||
|
];
|
||||||
|
|
||||||
|
protected array $aFieldTypes = [
|
||||||
|
'custom_config' => 'json',
|
||||||
|
'prev_day_width' => 'byte',
|
||||||
|
'node_bandwidth' => 'byte',
|
||||||
|
'node_bandwidth_limit' => 'byte'
|
||||||
|
];
|
||||||
|
|
||||||
|
protected array $aOrderBy = [
|
||||||
|
[
|
||||||
|
"sField" => "type",
|
||||||
|
"sSort" => "desc",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"sField" => "is_shadow",
|
||||||
|
"sSort" => "asc",
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
protected array $aJsonFields = [ // json字段
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
protected array $aDateFields = [ // date字段
|
||||||
|
// 'dtUpdatedAt'
|
||||||
|
];
|
||||||
|
|
||||||
|
protected array $aLenthFields = [ // 限制长度的字段
|
||||||
|
// "xx" => 100,
|
||||||
|
];
|
||||||
|
|
||||||
|
protected array $aSelectFields = [ // 作为默认select的字段
|
||||||
|
// 'iStatus',
|
||||||
|
// 'sComponentType',
|
||||||
|
];
|
||||||
|
|
||||||
|
// protected array $aValueFields = [ // 值映射
|
||||||
|
// 'sComponentType' => [
|
||||||
|
// 'TOP_BAR' => '顶级菜单',
|
||||||
|
// 'SIDEBAR_MENU' => '次级菜单',
|
||||||
|
// 'INNER_PAGE_TAB' => '页内菜单',
|
||||||
|
// ]
|
||||||
|
// ];
|
||||||
|
|
||||||
|
protected array $aCcExtFields = [
|
||||||
|
// 'dtCreatedAt' => '创建时间'
|
||||||
|
];
|
||||||
|
|
||||||
|
// protected array $aLimit = [3, 15, 40, 100]; // list长度,第一个为默认
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//CREATE TABLE `nasa_navigation` (
|
||||||
|
// `iId` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||||
|
// `iParentId` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '父级ID(0代表顶级导航)',
|
||||||
|
// `sName` varchar(64) NOT NULL COMMENT '导航菜单名称',
|
||||||
|
// `sSlug` varchar(128) NOT NULL DEFAULT '' COMMENT 'Laravel 路由别名(Route Name)',
|
||||||
|
// `sIcon` varchar(64) NOT NULL DEFAULT '' COMMENT '图标 Class/Svg 标识',
|
||||||
|
// `sGroup` varchar(128) NOT NULL DEFAULT '',
|
||||||
|
// `sComponentType` varchar(32) NOT NULL DEFAULT 'SIDEBAR_MENU' COMMENT '渲染组件类型:TOP_BAR, SIDEBAR_MENU, INNER_PAGE_TAB',
|
||||||
|
// `iSort` int(11) NOT NULL DEFAULT 0 COMMENT '排序权重(数值越小越靠前)',
|
||||||
|
// `iStatus` tinyint(4) NOT NULL DEFAULT 1 COMMENT '是否可见:1-显示, 0-隐藏',
|
||||||
|
// `sPermissionSlug` varchar(64) DEFAULT NULL COMMENT '权限标识(用于中间件拦截校验)',
|
||||||
|
// `dtCreatedAt` timestamp NOT NULL DEFAULT current_timestamp() COMMENT '创建时间',
|
||||||
|
// `dtUpdatedAt` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT '更新时间',
|
||||||
|
// PRIMARY KEY (`iId`),
|
||||||
|
// KEY `idx_status_parent_sort` (`iStatus`,`iParentId`,`iSort`),
|
||||||
|
// KEY `idx_permission` (`sPermissionSlug`)
|
||||||
|
//) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统功能导航路由表';
|
||||||
55
src/Middleware/VerifyNasaToken.php
Normal file
55
src/Middleware/VerifyNasaToken.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Middleware;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Http\Server\MiddlewareInterface;
|
||||||
|
use Psr\Http\Server\RequestHandlerInterface;
|
||||||
|
use Slim\Factory\AppFactory;
|
||||||
|
// use Illuminate\Support\Facades\Log; // 如果需要 Laravel 日志就留着,不要就删了
|
||||||
|
|
||||||
|
final class VerifyNasaToken implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 大厂工业范:完美对齐项目既有规范
|
||||||
|
*/
|
||||||
|
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||||
|
{
|
||||||
|
// 1. 严格使用项目既有命名风格,直接从 getHeaderLine 获取安全头
|
||||||
|
$iRequestTime = (int)$request->getHeaderLine('X-Nasa-Timestamp');
|
||||||
|
$sClientToken = $request->getHeaderLine('X-Nasa-Token');
|
||||||
|
|
||||||
|
// 2. 钢铁防御线一:缺少安全头直接熔断,直接沿用你模板里的 ->withJson 丝滑连招!
|
||||||
|
if (!$iRequestTime || !$sClientToken) {
|
||||||
|
return AppFactory::determineResponseFactory()->createResponse(403)->withJson([
|
||||||
|
'ret' => 0,
|
||||||
|
'msg' => 'Security Headers Missing.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 时效性防御:前后端时间差超过 60 秒判定为过期
|
||||||
|
if (abs(time() - $iRequestTime) > 60) {
|
||||||
|
return AppFactory::determineResponseFactory()->createResponse(403)->withJson([
|
||||||
|
'ret' => 0,
|
||||||
|
'msg' => 'Request Expired.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 验签防御:从环境变量掏出密钥进行像素级对撞
|
||||||
|
$sLocalSecret = getenv('nasa_secret') ?: ($_ENV['nasa_secret'] ?? '');
|
||||||
|
$sExpectedToken = md5("timestamp={$iRequestTime}&secret={$sLocalSecret}");
|
||||||
|
|
||||||
|
if ($sClientToken !== $sExpectedToken) {
|
||||||
|
return AppFactory::determineResponseFactory()->createResponse(403)->withJson([
|
||||||
|
'ret' => 0,
|
||||||
|
'msg' => 'Invalid Token.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 所有的关卡都过了,安全放行,通电!
|
||||||
|
return $handler->handle($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -191,4 +191,9 @@ final class Node extends Model
|
|||||||
return $oNow->diffInDays($oTargetDate);
|
return $oNow->diffInDays($oTargetDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMapAll() {
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user