feat:数据回收规则管理

This commit is contained in:
妙码生花 2022-04-03 12:38:25 +08:00
parent a6dc2a031e
commit d78cebf5c3
22 changed files with 426 additions and 7 deletions

View File

@ -1 +1,36 @@
<?php
if (!function_exists('get_controller_list')) {
function get_controller_list()
{
$controllerDir = app_path() . 'controller' . DIRECTORY_SEPARATOR;
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($controllerDir), \RecursiveIteratorIterator::LEAVES_ONLY
);
$controllerList = [];
foreach ($files as $file) {
if (!$file->isDir() && $file->getExtension() == 'php') {
$filePath = $file->getRealPath();
$name = str_replace($controllerDir, '', $filePath);
$name = str_replace(DIRECTORY_SEPARATOR, "/", $name);
$controllerList[$name] = $name;
}
}
return $controllerList;
}
}
if (!function_exists('get_table_list')) {
function get_table_list()
{
$tableList = [];
$tables = \think\facade\Db::query("SHOW TABLES");
foreach ($tables as $table) {
$tableList[reset($table)] = reset($table);
}
return $tableList;
}
}

View File

@ -0,0 +1,89 @@
<?php
namespace app\admin\controller\security;
use app\common\controller\Backend;
use app\admin\model\DataRecycle as DataRecycleModel;
use think\facade\Db;
class DataRecycle extends Backend
{
protected $model = null;
// 排除字段
protected $preExcludeFields = ['updatetime', 'createtime'];
protected $quickSearchField = 'name';
public function initialize()
{
parent::initialize();
$this->model = new DataRecycleModel();
}
/**
* 添加
*/
public function add()
{
if ($this->request->isPost()) {
parent::add();
}
// 放在add方法内就不需要额外添加权限节点了
$this->success('', [
'tables' => $this->getTableList(),
'controllers' => $this->getControllerList(),
]);
}
protected function getControllerList()
{
$outExcludeController = [
'Addon.php',
'Ajax.php',
'Dashboard.php',
'Index.php',
'routine/AdminInfo.php',
'user/MoneyLog.php',
'user/ScoreLog.php',
];
$outControllers = [];
$controllers = get_controller_list();
foreach ($controllers as $key => $controller) {
if (!in_array($controller, $outExcludeController)) {
$outControllers[$key] = $controller;
}
}
return $outControllers;
}
protected function getTableList()
{
$tablePrefix = config('database.connections.mysql.prefix');
$outExcludeTable = [
// 功能表
$tablePrefix . 'token',
$tablePrefix . 'captcha',
$tablePrefix . 'admin_group_access',
// 无删除功能
$tablePrefix . 'user_money_log',
$tablePrefix . 'user_score_log',
];
$outTables = [];
$tables = get_table_list();
foreach ($tables as $key => $table) {
if (!in_array($table, $outExcludeTable)) {
$outTables[$key] = $table;
}
}
return $outTables;
}
public function getPk($table = null)
{
$tablePk = Db::table($table)->getPk();
$this->success('ok', ['pk' => $tablePk]);
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace app\admin\model;
use think\Model;
class DataRecycle extends Model
{
protected $name = 'security_data_recycle';
protected $autoWriteTimestamp = 'int';
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
}

View File

@ -0,0 +1,14 @@
<?php
namespace app\admin\model;
use think\Model;
class DataRecycleLog extends Model
{
protected $name = 'security_data_recycle_log';
protected $autoWriteTimestamp = 'int';
protected $createTime = 'createtime';
protected $updateTime = false;
}

View File

@ -36,6 +36,11 @@ class UserMoneyLog extends model
$user->save();
}
public static function onBeforeDelete()
{
return false;
}
public function user()
{
return $this->belongsTo(User::class, 'user_id');

View File

@ -36,6 +36,11 @@ class UserScoreLog extends model
$user->save();
}
public static function onBeforeDelete()
{
return false;
}
public function user()
{
return $this->belongsTo(User::class, 'user_id');

View File

@ -0,0 +1,19 @@
import createAxios from '/@/utils/axios'
import { securityDataRecycle } from '/@/api/controllerUrls'
export function add() {
return createAxios({
url: securityDataRecycle + 'add',
method: 'get',
})
}
export function getPk(table: string) {
return createAxios({
url: securityDataRecycle + 'getPk',
method: 'get',
params: {
table: table,
},
})
}

View File

@ -12,3 +12,7 @@ export const userGroup = '/index.php/admin/user.group/'
export const userRule = '/index.php/admin/user.rule/'
export const userScoreLog = '/index.php/admin/user.scoreLog/'
export const userMoneyLog = '/index.php/admin/user.moneyLog/'
export const securityDataRecycle = '/index.php/admin/security.DataRecycle/'
export const securityDataRecycleLog = '/index.php/admin/security.DataRecycleLog/'
export const securitySensitiveData = '/index.php/admin/security.SensitiveData/'
export const securitySensitiveDataLog = '/index.php/admin/security.SensitiveDataLog/'

View File

@ -1,6 +1,6 @@
<script lang="ts">
import { createVNode, resolveComponent, defineComponent, PropType, VNode } from 'vue'
import { inputTypes, modelValuePropsTypes, modelValueTypes } from '/@/components/baInput'
import { inputTypes, modelValueTypes } from '/@/components/baInput'
import Array from '/@/components/baInput/array.vue'
export default defineComponent({
@ -16,7 +16,6 @@ export default defineComponent({
},
//
modelValue: {
type: modelValuePropsTypes,
required: true,
},
//

View File

@ -1,6 +1,6 @@
<script lang="ts">
import { createVNode, defineComponent, resolveComponent, PropType } from 'vue'
import { inputTypes, modelValuePropsTypes, modelValueTypes } from '/@/components/baInput'
import { inputTypes, modelValueTypes } from '/@/components/baInput'
import BaInput from '/@/components/baInput/index.vue'
export default defineComponent({
@ -20,7 +20,6 @@ export default defineComponent({
},
//
modelValue: {
type: modelValuePropsTypes,
required: true,
},
//
@ -118,9 +117,7 @@ export default defineComponent({
display: flex;
}
.ba-form-item-label {
display: flex;
align-items: center;
height: 100%;
display: inline-block;
.ba-form-item-label-tip {
padding-left: 6px;
font-size: 12px;

View File

@ -59,6 +59,8 @@ export default class baTable {
submitLoading: false,
// 默认表单数据(添加)
defaultItems: {},
// 表单字段加载状态
loading: false,
})
/* 表单状态-e */
@ -126,12 +128,14 @@ export default class baTable {
// 编辑
requestEdit = (id: string) => {
this.runBefore('requestEdit', { id })
this.form.loading = true
this.form.items = {}
return this.api
.edit({
id: id,
})
.then((res) => {
this.form.loading = false
this.form.items = res.data.row
this.runAfter('requestEdit', { res })
})

View File

@ -12,6 +12,7 @@
</div>
</template>
<div
v-loading="baTable.form.loading"
class="ba-operate-form"
:class="'ba-' + baTable.form.operate + '-form'"
:style="'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'"

View File

@ -13,6 +13,7 @@
</div>
</template>
<div
v-loading="baTable.form.loading"
class="ba-operate-form"
:class="'ba-' + baTable.form.operate + '-form'"
:style="'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'"

View File

@ -13,6 +13,7 @@
</div>
</template>
<div
v-loading="baTable.form.loading"
class="ba-operate-form"
:class="'ba-' + baTable.form.operate + '-form'"
:style="'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'"

View File

@ -12,6 +12,7 @@
</div>
</template>
<div
v-loading="baTable.form.loading"
class="ba-operate-form"
:class="'ba-' + baTable.form.operate + '-form'"
:style="'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'"

View File

@ -0,0 +1,120 @@
<template>
<!-- 对话框表单 -->
<el-dialog
custom-class="ba-operate-dialog"
:close-on-click-modal="false"
:model-value="baTable.form.operate ? true : false"
@close="baTable.toggleForm"
>
<template #title>
<div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
{{ baTable.form.operate ? t(baTable.form.operate) : '' }}
</div>
</template>
<div
v-loading="baTable.form.loading"
class="ba-operate-form"
:class="'ba-' + baTable.form.operate + '-form'"
:style="'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'"
>
<el-form
v-if="!baTable.form.loading"
ref="formRef"
@keyup.enter="baTable.onSubmit(formRef)"
:model="baTable.form.items"
label-position="right"
:label-width="baTable.form.labelWidth + 'px'"
:rules="rules"
>
<FormItem
label="规则名称"
type="string"
v-model="baTable.form.items!.name"
:attr="{ prop: 'name' }"
:input-attr="{ placeholder: '规则名称有助于后续识别被删数据' }"
/>
<FormItem
label="控制器"
type="select"
v-model="baTable.form.items!.controller"
:attr="{ prop: 'controller' }"
:data="{ content: formData.controllerList }"
:input-attr="{ placeholder: '数据回收机制将监控此控制器下的删除操作' }"
/>
<FormItem
label="对应数据表"
type="select"
v-model="baTable.form.items!.data_table"
:attr="{ prop: 'data_table' }"
:data="{ content: formData.tableList }"
:input-attr="{ onChange: onTableChange }"
/>
<FormItem label="数据表主键" type="string" v-model="baTable.form.items!.primary_key" :attr="{ prop: 'primary_key' }" />
<FormItem
label="状态"
type="radio"
v-model="baTable.form.items!.status"
:attr="{ prop: 'status' }"
:data="{ content: { '0': '禁用', '1': '启用' } }"
/>
</el-form>
</div>
<template #footer>
<div :style="'width: calc(100% - ' + baTable.form.labelWidth! / 1.8 + 'px)'">
<el-button @click="baTable.toggleForm('')">取消</el-button>
<el-button v-blur :loading="baTable.form.submitLoading" @click="baTable.onSubmit(formRef)" type="primary">
{{ baTable.form.operateIds && baTable.form.operateIds.length > 1 ? '保存并编辑下一项' : '保存' }}
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { reactive, ref, inject } from 'vue'
import { useI18n } from 'vue-i18n'
import type baTableClass from '/@/utils/baTable'
import FormItem from '/@/components/formItem/index.vue'
import { FormItemRule } from 'element-plus/es/components/form/src/form.type'
import type { ElForm } from 'element-plus'
import { buildValidatorData } from '/@/utils/validate'
import { getPk } from '/@/api/backend/security/dataRecycle'
interface Props {
formData: {
tableList?: anyObj
controllerList?: anyObj
}
}
const props = withDefaults(defineProps<Props>(), {
formData: () => {
return {}
},
})
const formRef = ref<InstanceType<typeof ElForm>>()
const baTable = inject('baTable') as baTableClass
const { t } = useI18n()
const rules: Partial<Record<string, FormItemRule[]>> = reactive({
name: [buildValidatorData('required', '规则名称')],
controller: [buildValidatorData('required', '', 'change', '请选择控制器')],
data_table: [buildValidatorData('required', '', 'change', '请选择数据表')],
primary_key: [buildValidatorData('required', '数据表主键', 'change')],
status: [buildValidatorData('required', '数据表主键')],
})
const onTableChange = (val: string) => {
getPk(val).then((res) => {
baTable.form.items!.primary_key = res.data.pk
baTable.form.defaultItems!.primary_key = res.data.pk
})
}
</script>
<style scoped lang="scss">
.ba-el-radio {
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,106 @@
<template>
<div class="default-main ba-table-box">
<el-alert class="ba-table-alert" v-if="baTable.table.remark" :title="baTable.table.remark" type="info" show-icon />
<!-- 表格顶部菜单 -->
<TableHeader
:buttons="['refresh', 'add', 'edit', 'delete', 'comSearch']"
:quick-search-placeholder="'通过规则名模糊搜索'"
@action="baTable.onTableHeaderAction"
/>
<!-- 表格 -->
<!-- 要使用`el-table`组件原有的属性直接加在Table标签上即可 -->
<Table ref="tableRef" @action="baTable.onTableAction" />
<!-- 表单 -->
<Form ref="formRef" :form-data="addFormData" />
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, reactive, provide } from 'vue'
import baTableClass from '/@/utils/baTable'
import { securityDataRecycle } from '/@/api/controllerUrls'
import { add } from '/@/api/backend/security/dataRecycle'
import Form from './form.vue'
import Table from '/@/components/table/index.vue'
import TableHeader from '/@/components/table/header/index.vue'
import { defaultOptButtons } from '/@/components/table'
import { baTableApi } from '/@/api/common'
const tableRef = ref()
const formRef = ref()
const baTable = new baTableClass(
new baTableApi(securityDataRecycle),
{
column: [
{ type: 'selection', align: 'center', operator: false },
{ label: 'ID', prop: 'id', align: 'center', operator: 'LIKE', operatorPlaceholder: '模糊查询', width: 70 },
{ label: '规则名称', prop: 'name', align: 'center', operator: 'LIKE', operatorPlaceholder: '模糊查询' },
{ label: '控制器', prop: 'controller', align: 'center', operator: 'LIKE', operatorPlaceholder: '模糊查询' },
{ label: '数据表', prop: 'data_table', align: 'center', operator: 'LIKE', operatorPlaceholder: '模糊查询' },
{ label: '数据表主键', prop: 'primary_key', align: 'center', operator: 'LIKE', operatorPlaceholder: '模糊查询' },
{
label: '状态',
prop: 'status',
align: 'center',
render: 'tag',
custom: { '0': 'danger', '1': 'success' },
replaceValue: { '0': '禁用', '1': '删除监控中' },
},
{ label: '更新时间', prop: 'updatetime', align: 'center', render: 'datetime', sortable: 'custom', operator: 'RANGE', width: 160 },
{ label: '创建时间', prop: 'createtime', align: 'center', render: 'datetime', sortable: 'custom', operator: 'RANGE', width: 160 },
{
label: '操作',
align: 'center',
width: '130',
render: 'buttons',
buttons: defaultOptButtons(['edit', 'delete']),
operator: false,
},
],
dblClickNotEditColumn: [undefined, 'status'],
},
{
defaultItems: {
status: '1',
},
},
{
//
toggleForm: ({ operate }: { operate: string }) => {
if (operate == 'add' || operate == 'edit') {
baTable.form.loading = true
add().then((res) => {
addFormData.tableList = res.data.tables
addFormData.controllerList = res.data.controllers
baTable.form.loading = false
})
}
},
}
)
const addFormData = reactive({
tableList: {},
controllerList: {},
})
provide('baTable', baTable)
onMounted(() => {
baTable.table.ref = tableRef.value
baTable.mount()
baTable.getIndex()
})
</script>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'security/dataRecycle',
})
</script>
<style scoped lang="scss"></style>

View File

@ -13,6 +13,7 @@
</div>
</template>
<div
v-loading="baTable.form.loading"
class="ba-operate-form"
:class="'ba-' + baTable.form.operate + '-form'"
:style="'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'"

View File

@ -13,6 +13,7 @@
</div>
</template>
<div
v-loading="baTable.form.loading"
class="ba-operate-form"
:class="'ba-' + baTable.form.operate + '-form'"
:style="'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'"

View File

@ -13,6 +13,7 @@
</div>
</template>
<div
v-loading="baTable.form.loading"
class="ba-operate-form"
:class="'ba-' + baTable.form.operate + '-form'"
:style="'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'"

View File

@ -28,6 +28,7 @@ declare global {
items?: anyObj
submitLoading?: boolean
defaultItems?: anyObj
loading?: boolean
}
/* BaTable前置处理函数(前置埋点) */