feat: new feature

This commit is contained in:
JaguarJack
2022-12-23 19:47:13 +08:00
parent 17f2dc4d3c
commit 81fac9f62c
38 changed files with 1019 additions and 1486 deletions

View File

@@ -5,9 +5,13 @@ namespace Modules\User\Http\Controllers;
use Catch\Base\CatchController as Controller;
use Catch\Support\Module\ModuleRepository;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\Request;
use Modules\User\Models\LogLogin;
use Modules\User\Models\LogOperate;
use Modules\User\Models\User;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class UserController extends Controller
{
@@ -113,12 +117,26 @@ class UserController extends Controller
/**
* login log
* @param LogLogin $logLogin
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
* @return LengthAwarePaginator
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function loginLog(LogLogin $logLogin)
{
return $logLogin->getUserLogBy($this->getLoginUser()->email);
$user = $this->getLoginUser();
return $logLogin->getUserLogBy($user->isSuperAdmin() ? null : $user->email);
}
public function operateLog(LogOperate $logOperate, Request $request)
{
$scope = $request->get('scope', 'self');
return $logOperate->setBeforeGetList(function ($builder) use ($scope){
if ($scope == 'self') {
return $builder->where('creator_id', $this->getLoginUserId());
}
return $builder;
})->getList();
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Modules\User\Middlewares;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Modules\User\Models\LogOperate;
class OperatingMiddleware
{
/**
*
* @param $request
* @param Closure $next
* @return mixed
*/
public function handle($request, Closure $next): mixed
{
return $next($request);
}
/**
*
* @param Request $request
* @param Response $response
*/
public function terminate(Request $request, Response $response): void
{
app(LogOperate::class)->log($request, $response);
}
}

View File

@@ -24,14 +24,15 @@ class LogLogin extends Model
/**
*
* @param string $email
* @param ?string $email
* @return LengthAwarePaginator
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function getUserLogBy(string $email): LengthAwarePaginator
public function getUserLogBy(?string $email): LengthAwarePaginator
{
return self::query()->where('account', $email)
->paginate(request()->get('limit', 10));
return static::when($email, function ($query) use ($email){
$query->where('account', $email);
})->paginate(request()->get('limit', 10));
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace Modules\User\Models;
use Catch\CatchAdmin;
use Catch\Traits\DB\BaseOperate;
use Catch\Traits\DB\ScopeTrait;
use Catch\Traits\DB\Trans;
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
use Symfony\Component\HttpFoundation\Response;
class LogOperate extends Model
{
use BaseOperate, Trans, ScopeTrait;
protected $table = 'log_operate';
protected $fillable = [
'id',
'module',
'action',
'params',
'ip',
'http_method',
'http_code',
'start_at',
'time_taken',
'creator_id',
'created_at',
];
/**
* @param Request $request
* @param Response $response
* @return void
*/
public function log(Request $request, Response $response): void
{
$user = Auth::guard(getGuardName())->user();
$userModel = getAuthUserModel();
if (! $user instanceof $userModel) {
return;
}
[$module, $controller, $action] = CatchAdmin::parseFromRouteAction();
$requestStartAt = app(Kernel::class)->requestStartedAt()->getPreciseTimestamp(3);
$params = $request->all();
// 如果参数过长则不记录
if (!empty($params)) {
if (strlen(\json_encode($params, JSON_UNESCAPED_UNICODE)) > 5000) {
$params = [];
}
}
$timeTaken = intval(microtime(true) * 1000 - $requestStartAt);
$this->storeBy([
'module' => $module,
'action' => $controller . '@' . $action,
'creator_id' => $user->id,
'http_method' => $request->method(),
'http_code' => $response->getStatusCode(),
'start_at' => $requestStartAt,
'time_taken' => $timeTaken,
'ip' => $request->ip(),
'params' => \json_encode($params, JSON_UNESCAPED_UNICODE),
'created_at' => time()
]);
}
/**
*
* @return Attribute
*/
protected function timeTaken(): Attribute
{
return Attribute::make(
get: fn ($value) => $value > 1000 ? intval($value/1000) . 's' : $value . 'ms',
);
}
}

View File

@@ -7,7 +7,7 @@ use Catch\Support\Module\ModuleRepository;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Route;
use Modules\Permissions\Models\PermissionsModel;
use Modules\Permissions\Models\Permissions;
trait UserRelations
{
@@ -51,7 +51,7 @@ trait UserRelations
*/
public function withPermissions(): self
{
/* @var PermissionsModel $permissionsModel */
/* @var Permissions $permissionsModel */
$permissionsModel = app($this->getPermissionsModel());
if ($this->isSuperAdmin()) {
@@ -61,7 +61,7 @@ trait UserRelations
app($this->getRolesModel())->with(['permissions'])->get()
->each(function ($role) use (&$permissions){
->each(function ($role) use (&$permissions) {
$permissions = $permissions->concat($role->permissions);
});
@@ -87,7 +87,7 @@ trait UserRelations
}
if ($this->isSuperAdmin()) {
// return true;
return true;
}
$this->withPermissions();
@@ -108,34 +108,34 @@ trait UserRelations
/**
* get RolesModel
*
* @see \Modules\Permissions\Models\RolesModel
* @see \Modules\Permissions\Models\Roles
* @return string
*/
protected function getRolesModel(): string
{
return '\\'.CatchAdmin::getModuleModelNamespace('permissions').'RolesModel';
return '\\'.CatchAdmin::getModuleModelNamespace('permissions').'Roles';
}
/**
* get JobsModel
*
* @see \Modules\Permissions\Models\JobsModel
* @see \Modules\Permissions\Models\Jobs
* @return string
*/
protected function getJobsModel(): string
{
return '\\'.CatchAdmin::getModuleModelNamespace('permissions').'JobsModel';
return '\\'.CatchAdmin::getModuleModelNamespace('permissions').'Jobs';
}
/**
* get PermissionsModel
*
* @see \Modules\Permissions\Models\PermissionsModel
* @return string
*@see \Modules\Permissions\Models\Permissions
*/
protected function getPermissionsModel(): string
{
return '\\'.CatchAdmin::getModuleModelNamespace('permissions').'PermissionsModel';
return '\\'.CatchAdmin::getModuleModelNamespace('permissions').'Permissions';
}
}

View File

@@ -6,6 +6,7 @@ use Catch\CatchAdmin;
use Catch\Providers\CatchModuleServiceProvider;
use Modules\User\Events\Login;
use Modules\User\Listeners\Login as LoginListener;
use Modules\User\Middlewares\OperatingMiddleware;
class UserServiceProvider extends CatchModuleServiceProvider
{
@@ -24,9 +25,11 @@ class UserServiceProvider extends CatchModuleServiceProvider
return CatchAdmin::getModuleRoutePath('user');
}
public function registerEvents(array $events): void
/**
* @return string[]
*/
protected function middlewares(): array
{
parent::registerEvents($events); // TODO: Change the autogenerated stub
return [OperatingMiddleware::class];
}
}

View File

@@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('log_operate', function (Blueprint $table) {
$table->increments('id');
$table->string('module', 50)->comment('操作');
$table->string('action', 50)->comment('操作');
$table->text('params')->comment('参数');
$table->string('ip')->comment('ip 地址');
$table->string('http_method', 10)->comment('http 请求方式');
$table->smallInteger('http_code')->comment('http status code');
$table->unsignedInteger('start_at')->comment('请求开始时间');
$table->smallInteger('time_taken')->comment('请求消耗时间/ms');
$table->creatorId();
$table->createdAt();
$table->engine = 'InnoDB';
$table->comment('操作日志');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('log_operate');
}
};

View File

@@ -13,3 +13,5 @@ Route::apiResource('users', UserController::class);
Route::put('users/enable/{id}', [UserController::class, 'enable']);
Route::match(['post', 'get'], 'user/online', [UserController::class, 'online']);
Route::get('user/login/log', [UserController::class, 'loginLog']);
Route::get('user/operate/log', [UserController::class, 'operateLog']);

View File

@@ -1,30 +1,20 @@
<template>
<el-table :data="tableData" class="mt-3" v-loading="loading">
<el-table-column prop="account" label="账户" width="150px" />
<el-table-column prop="browser" label="浏览器" width="100px" />
<el-table-column prop="platform" label="平台" width="100px" />
<el-table-column prop="login_ip" label="IP" width="120px" />
<el-table-column prop="status" label="状态" width="100px">
<template #default="scope">
<el-tag type="success" v-if="scope.row.status === 1">成功</el-tag>
<el-tag type="danger" v-else>失败</el-tag>
</template>
</el-table-column>
<el-table-column prop="login_at" label="登录时间" />
</el-table>
<div class="pl-2 pr-2 bg-white dark:bg-regal-dark rounded-lg mt-4 pb-6">
<el-table :data="tableData" class="mt-3" v-loading="loading">
<el-table-column prop="account" label="账户" width="150px" />
<el-table-column prop="browser" label="浏览器" width="100px" />
<el-table-column prop="platform" label="平台" width="100px" />
<el-table-column prop="login_ip" label="IP" width="120px" />
<el-table-column prop="status" label="状态" width="100px">
<template #default="scope">
<el-tag type="success" v-if="scope.row.status === 1">成功</el-tag>
<el-tag type="danger" v-else>失败</el-tag>
</template>
</el-table-column>
<el-table-column prop="login_at" label="登录时间" />
</el-table>
<div class="pt-2 pb-2 flex justify-end">
<el-pagination
background
v-if="total > query.limit"
layout="total,sizes,prev, pager,next"
:current-page="query.page"
:page-size="query.limit"
@current-change="changePage"
@size-change="changeLimit"
:total="total"
:page-sizes="[10, 20, 30, 50]"
/>
<Paginate />
</div>
</template>
@@ -34,12 +24,11 @@ import { useGetList } from '/admin/composables/curd/useGetList'
const api = 'user/login/log'
const { data, query, search, changePage, changeLimit, loading } = useGetList(api)
const { data, query, search, reset, loading } = useGetList(api)
onMounted(() => search())
const tableData = computed(() => data.value?.data)
const total = computed(() => data.value?.total)
</script>
<style scoped></style>

View File

@@ -1,28 +1,32 @@
<template>
<el-table :data="tableData" class="mt-3" v-loading="loading">
<el-table-column prop="username" label="用户名" width="180" />
<el-table-column prop="avatar" label="头像" width="180" />
<el-table-column prop="email" label="邮箱" />
<el-table-column prop="status" label="状态">
<template #default="scope">
<Status v-model="scope.row.status" :id="scope.row.id" :api="api" />
</template>
</el-table-column>
<el-table-column prop="created_at" label="创建时间" />
</el-table>
<div class="pt-2 pb-2 flex justify-end">
<el-pagination
background
v-if="total > query.limit"
layout="total,sizes,prev, pager,next"
:current-page="query.page"
:page-size="query.limit"
@current-change="changePage"
@size-change="changeLimit"
:total="total"
:page-sizes="[10, 20, 30, 50]"
/>
<div class="pl-2 pr-2 bg-white dark:bg-regal-dark rounded-lg mt-2 pb-6">
<div class="w-full flex justify-end">
<el-radio-group v-model="query.scope" size="small" @change="search">
<el-radio-button label="self">只看自己</el-radio-button>
<el-radio-button label="all">全部</el-radio-button>
</el-radio-group>
</div>
<el-table :data="tableData" class="mt-3" v-loading="loading">
<el-table-column prop="creator" label="创建人" />
<el-table-column prop="module" label="模块" />
<el-table-column prop="action" label="操作" width="150" />
<el-table-column prop="http_method" label="请求方法" width="90" />
<el-table-column prop="http_code" label="请求状态" width="90">
<template #default="scope">
<el-tag type="success" v-if="scope.row.http_code >= 200 && scope.row.http_code < 300"> {{ scope.row.http_code }}</el-tag>
<el-tag type="danger" v-else>{{ scope.row.http_code }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="time_taken" label="耗时" />
<el-table-column prop="params" label="参数">
<template #default="scope">
<el-tooltip class="box-item" effect="dark" :content="scope.row.params" placement="top-start">
<el-button size="small" type="primary">查看</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<Paginate />
</div>
</template>
@@ -30,14 +34,15 @@
import { computed, onMounted } from 'vue'
import { useGetList } from '/admin/composables/curd/useGetList'
const api = 'users'
const api = 'user/operate/log'
const { data, query, search, reset, changePage, changeLimit, loading } = useGetList(api)
const { data, query, search, reset, loading } = useGetList(api)
query.value.scope = 'self'
onMounted(() => search())
const tableData = computed(() => data.value?.data)
const total = computed(() => data.value?.total)
</script>
<style scoped></style>