feat:Gen文件模型层生成重构、TOOL类文件新增字符串替换优化方法、Gen文件部分方法修饰符修改,代码生成入口模型层生成方式修改
This commit is contained in:
parent
d9e8c59651
commit
cd484d6f8d
@ -8,6 +8,7 @@ use app\Request;
|
|||||||
use app\exception\ErrorMsg;
|
use app\exception\ErrorMsg;
|
||||||
use think\facade\Db;
|
use think\facade\Db;
|
||||||
use app\common\exception\Tool;
|
use app\common\exception\Tool;
|
||||||
|
use app\admin\controller\Gen\GenApi\GenModel\Model;
|
||||||
|
|
||||||
class Gen extends BaseController
|
class Gen extends BaseController
|
||||||
{
|
{
|
||||||
@ -344,12 +345,14 @@ class Gen extends BaseController
|
|||||||
try {
|
try {
|
||||||
if ($table['tplCategory'] == "web_static") {
|
if ($table['tplCategory'] == "web_static") {
|
||||||
$this->createAdminController($columns, $table);
|
$this->createAdminController($columns, $table);
|
||||||
$this->createNewModel($columns, $table);
|
// $this->createNewModel($columns, $table);
|
||||||
|
(new Model($columns, $table))->createNewModel();
|
||||||
$this->createJsWebStaticIndex($columns, $table);
|
$this->createJsWebStaticIndex($columns, $table);
|
||||||
$this->createJsApi($columns, $table);
|
$this->createJsApi($columns, $table);
|
||||||
} else if ($table['tplCategory'] == "crud") {
|
} else if ($table['tplCategory'] == "crud") {
|
||||||
$this->createAdminController($columns, $table);
|
$this->createAdminController($columns, $table);
|
||||||
$this->createNewModel($columns, $table);
|
// $this->createNewModel($columns, $table);
|
||||||
|
(new Model($columns, $table))->createNewModel();
|
||||||
$this->createJsVue($columns, $table);
|
$this->createJsVue($columns, $table);
|
||||||
$this->createJsAdd($columns, $table);
|
$this->createJsAdd($columns, $table);
|
||||||
$this->createJsEdit($columns, $table);
|
$this->createJsEdit($columns, $table);
|
||||||
@ -1605,7 +1608,7 @@ class Gen extends BaseController
|
|||||||
|
|
||||||
|
|
||||||
//创建文件夹
|
//创建文件夹
|
||||||
private function mkdir($path)
|
public function mkdir($path)
|
||||||
{
|
{
|
||||||
if (!is_dir($path)) {
|
if (!is_dir($path)) {
|
||||||
if (false === @mkdir($path, 0777, true) && !is_dir($path)) {
|
if (false === @mkdir($path, 0777, true) && !is_dir($path)) {
|
||||||
@ -1874,7 +1877,7 @@ class Gen extends BaseController
|
|||||||
* @param array $service_fields 业务字段
|
* @param array $service_fields 业务字段
|
||||||
* @param int $type 模板字符类型 1:一维数组格式 2:验证器格式 3:列表条件查询 4:关联数组格式
|
* @param int $type 模板字符类型 1:一维数组格式 2:验证器格式 3:列表条件查询 4:关联数组格式
|
||||||
*/
|
*/
|
||||||
private static function toFormTempStr(array $arr, array $service_fields, int $type = 1)
|
public static function toFormTempStr(array $arr, array $service_fields, int $type = 1)
|
||||||
{
|
{
|
||||||
$str = "";
|
$str = "";
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
@ -1988,7 +1991,7 @@ class Gen extends BaseController
|
|||||||
\$params = \$request->param();
|
\$params = \$request->param();
|
||||||
\$select = Model{$class_name}::field({$init_fields_arr_str})
|
\$select = Model{$class_name}::field({$init_fields_arr_str})
|
||||||
->order({$order_field}, $order_mode )
|
->order({$order_field}, $order_mode )
|
||||||
->select();
|
->select()->toArray();
|
||||||
return Model{$class_name}::exportExcel(\$select);
|
return Model{$class_name}::exportExcel(\$select);
|
||||||
}";
|
}";
|
||||||
case 'export_mod':
|
case 'export_mod':
|
||||||
|
215
app/admin/controller/Gen/GenApi/GenApi.php
Normal file
215
app/admin/controller/Gen/GenApi/GenApi.php
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\Gen\GenApi;
|
||||||
|
|
||||||
|
use app\admin\controller\Gen\Gen;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后端生成类
|
||||||
|
*/
|
||||||
|
class GenApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 项目根目录路径
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $root;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段信息
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fields = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成信息
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $table = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实体类名
|
||||||
|
* @var string 例:User
|
||||||
|
*/
|
||||||
|
protected $class_name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模块名
|
||||||
|
* @var string 例:User
|
||||||
|
*/
|
||||||
|
protected $module_name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务名 例:user
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $business_name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方法名
|
||||||
|
* @var string 例:用户
|
||||||
|
*/
|
||||||
|
protected $function_name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型层模板读取路径
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $model_temp_path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型层模块生成路径
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $model_module_path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型层生成路径
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $model_path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务字段
|
||||||
|
* @var array [ 'user_name' => '用户名称',...]
|
||||||
|
*/
|
||||||
|
protected $business_fields = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增允许字段模板字符
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $add_fields_temp = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否导入
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $is_import = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否导出
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $is_export = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片字段数组
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $img_fields = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造器
|
||||||
|
*
|
||||||
|
* @param array $fields 字段信息
|
||||||
|
* @param array $table 生成信息
|
||||||
|
*/
|
||||||
|
public function __construct(array $fields, array $table)
|
||||||
|
{
|
||||||
|
$this->root = base_path();
|
||||||
|
$this->fields = $fields;
|
||||||
|
$this->table = $table;
|
||||||
|
$this->class_name = $this->table['className'];
|
||||||
|
$this->module_name = $this->table['moduleName'];
|
||||||
|
$this->business_name = $this->table['businessName'];
|
||||||
|
$this->function_name = $this->table['functionName'];
|
||||||
|
$this->model_temp_path = self::initFilePath("{$this->root}resources/view/admin/model.tpl");
|
||||||
|
$this->model_module_path = self::initFilePath("{$this->root}common/model/{$this->module_name}");
|
||||||
|
$this->model_path = self::initFilePath("{$this->model_module_path}/{$this->class_name}.php");
|
||||||
|
$this->mkdirModelModule();
|
||||||
|
$this->buildBusinessFields();
|
||||||
|
$this->buildAddFieldsTemp();
|
||||||
|
$this->initImportExportStatus();
|
||||||
|
$this->initImgFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化图片字段变量
|
||||||
|
*/
|
||||||
|
private function initImgFields(): void
|
||||||
|
{
|
||||||
|
foreach ($this->fields as $val) {
|
||||||
|
if ($val['htmlType'] == 'imageUpload') {
|
||||||
|
$this->img_fields[] = $val['columnName'];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化导入导出状态
|
||||||
|
*/
|
||||||
|
private function initImportExportStatus(): void
|
||||||
|
{
|
||||||
|
//其他选项 4:导出 6:导入
|
||||||
|
$checked_btn_arr = $this->table['options']->CheckedBtn;
|
||||||
|
$this->is_import = in_array('6', $checked_btn_arr);
|
||||||
|
$this->is_export = in_array('4', $checked_btn_arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建新增允许字段模板字符
|
||||||
|
*/
|
||||||
|
private function buildAddFieldsTemp(): void
|
||||||
|
{
|
||||||
|
$this->add_fields_temp .= '[';
|
||||||
|
foreach ($this->business_fields as $key => $val) {
|
||||||
|
$this->add_fields_temp .= "'{$key}',";
|
||||||
|
};
|
||||||
|
$this->add_fields_temp .= ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建业务字段名
|
||||||
|
*/
|
||||||
|
private function buildBusinessFields()
|
||||||
|
{
|
||||||
|
foreach ($this->fields as $val) {
|
||||||
|
if (!$val['isInit']) {
|
||||||
|
$this->business_fields[$val['columnName']] = $val['columnComment'];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成模型层模块文件夹
|
||||||
|
*/
|
||||||
|
private function mkdirModelModule()
|
||||||
|
{
|
||||||
|
self::mkdir($this->model_module_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化文件路径
|
||||||
|
*
|
||||||
|
* @param string $path 文件路径
|
||||||
|
* @return string 初始化后的文件路径
|
||||||
|
*/
|
||||||
|
protected static function initFilePath(string $path): string
|
||||||
|
{
|
||||||
|
//系统分隔符替换
|
||||||
|
return str_replace('/', DIRECTORY_SEPARATOR, $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建文件夹
|
||||||
|
*
|
||||||
|
* @param string $path 文件夹创建所属路径
|
||||||
|
*/
|
||||||
|
protected static function mkdir(string $path): void
|
||||||
|
{
|
||||||
|
if (!(new Gen(new \think\App()))->mkdir($path)) {
|
||||||
|
throwErrorMsg(__METHOD__ . "创建文件夹失败!");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组转换为模板字符串
|
||||||
|
* @param array $arr 数组
|
||||||
|
* @param int $type 模板字符类型 1:一维数组格式 2:验证器格式 3:列表条件查询 4:关联数组格式
|
||||||
|
*/
|
||||||
|
protected function toFormTempStr(array $arr, int $type = 1): string
|
||||||
|
{
|
||||||
|
return (new Gen(new \think\App()))->toFormTempStr($arr, $this->business_fields, $type);
|
||||||
|
}
|
||||||
|
}
|
13
app/admin/controller/Gen/GenApi/GenController/Admin.php
Normal file
13
app/admin/controller/Gen/GenApi/GenController/Admin.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\Gen\GenApi\GenController;
|
||||||
|
|
||||||
|
use app\admin\controller\Gen\GenApi\GenApi;
|
||||||
|
use app\common\exception\Tool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后端后台接口生成类
|
||||||
|
*/
|
||||||
|
class Admin extends GenApi
|
||||||
|
{
|
||||||
|
}
|
13
app/admin/controller/Gen/GenApi/GenController/Api.php
Normal file
13
app/admin/controller/Gen/GenApi/GenController/Api.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\Gen\GenApi\GenController;
|
||||||
|
|
||||||
|
use app\admin\controller\Gen\GenApi\GenApi;
|
||||||
|
use app\common\exception\Tool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后端前台接口生成类
|
||||||
|
*/
|
||||||
|
class Api extends GenApi
|
||||||
|
{
|
||||||
|
}
|
212
app/admin/controller/Gen/GenApi/GenModel/Model.php
Normal file
212
app/admin/controller/Gen/GenApi/GenModel/Model.php
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\Gen\GenApi\GenModel;
|
||||||
|
|
||||||
|
use app\admin\controller\Gen\GenApi\GenApi;
|
||||||
|
use app\common\exception\Tool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后端模型层生成类
|
||||||
|
*/
|
||||||
|
class Model extends GenApi
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 生成模型层
|
||||||
|
*/
|
||||||
|
public function createNewModel(): void
|
||||||
|
{
|
||||||
|
//打开模型层模板文件,拿到该文件资源(r只读方式打开)
|
||||||
|
$tem_f = fopen($this->model_temp_path, "r");
|
||||||
|
//读取模型层模板文件,拿到模板文件的全部字符串
|
||||||
|
$temp_str = fread($tem_f, filesize($this->model_temp_path));
|
||||||
|
//模板文件字符串替换
|
||||||
|
$temp_str = Tool::strReplacePlus($temp_str, [
|
||||||
|
//实体类名
|
||||||
|
'{$className}' => $this->class_name,
|
||||||
|
//模块名
|
||||||
|
'{$moduleName}' => $this->module_name,
|
||||||
|
//业务名
|
||||||
|
'{$businessName}' => $this->business_name,
|
||||||
|
//模型层字段信息
|
||||||
|
'{$fields}' => $this->buildFieldsInfoTemp(),
|
||||||
|
//模型层导出Excel方法
|
||||||
|
'{$exportExcelContent}' => $this->buildExportFun(),
|
||||||
|
//模型层导入/下载模板Excel表头
|
||||||
|
'{$importExcelField}' => $this->buildImportDownloadFieldsTemp(),
|
||||||
|
//模型层导入Excel方法
|
||||||
|
'{$importExcelContent}' => $this->buildImportFunTemp(),
|
||||||
|
//模型层导入Excel初始化方法
|
||||||
|
'{$importExcelInitContent}' => $this->buildImportInitFunTemp(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
//打开模型层文件,拿到该文件资源(w写入方式打开)
|
||||||
|
$gen_model = fopen($this->model_path, 'w');
|
||||||
|
//写入该模型层文件
|
||||||
|
fwrite($gen_model, $temp_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建模型层字段信息模板字符
|
||||||
|
*/
|
||||||
|
public function buildFieldsInfoTemp(): string
|
||||||
|
{
|
||||||
|
$str = "";
|
||||||
|
foreach ($this->fields as $value) {
|
||||||
|
$str .= " '{$value['columnName']}' => '{$value['columnType']}' , \n";
|
||||||
|
}
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建模型层导出方法模板字符
|
||||||
|
*/
|
||||||
|
private function buildExportFun(): string
|
||||||
|
{
|
||||||
|
if (!$this->is_export) return '';
|
||||||
|
|
||||||
|
//excel表头字符构建
|
||||||
|
$excel_header = [];
|
||||||
|
foreach ($this->business_fields as $name) {
|
||||||
|
$excel_header[] = $name;
|
||||||
|
};
|
||||||
|
$excel_header_temp = $this->toFormTempStr($excel_header);
|
||||||
|
|
||||||
|
//导出数据分配
|
||||||
|
$data_str = '';
|
||||||
|
foreach ($this->business_fields as $field => $name) {
|
||||||
|
if (in_array($name, $this->img_fields)) {
|
||||||
|
$data_str .= "Excel::ExportImgFiled(\$val['$field']),\n";
|
||||||
|
} else {
|
||||||
|
$data_str .= "\$val['{$field}'],\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$data_str = "[\n{$data_str}]";
|
||||||
|
|
||||||
|
//模板字符构建并返回
|
||||||
|
return "
|
||||||
|
/**
|
||||||
|
* 导出Excel
|
||||||
|
*
|
||||||
|
* @param array \$select 导出的数据
|
||||||
|
*/
|
||||||
|
public static function exportExcel(array \$select): void
|
||||||
|
{
|
||||||
|
\$data = [{$excel_header_temp}];
|
||||||
|
foreach (\$select as \$key => \$val) {
|
||||||
|
\$data[] = {$data_str};
|
||||||
|
}
|
||||||
|
\$excel = (new Excel())->exporTsheet(\$data);
|
||||||
|
\$excel->save('{$this->function_name}.xlsx');
|
||||||
|
}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建导入/下载模板表头模板字符
|
||||||
|
*/
|
||||||
|
private function buildImportDownloadFieldsTemp(): string
|
||||||
|
{
|
||||||
|
if (!$this->is_import) return '';
|
||||||
|
|
||||||
|
$init_fields_str = $this->toFormTempStr($this->business_fields, 4);
|
||||||
|
$init_fields_str = substr($init_fields_str, 0, strlen($init_fields_str) - 1); //末尾的逗号去除
|
||||||
|
return "
|
||||||
|
// excel导入/下载模板表头
|
||||||
|
public const EXCELFIELD = {$init_fields_str};
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建模型层导入方法模板字符
|
||||||
|
*/
|
||||||
|
private function buildImportFunTemp(): string
|
||||||
|
{
|
||||||
|
if (!$this->is_import) return '';
|
||||||
|
|
||||||
|
//模板字符构建并返回
|
||||||
|
return "
|
||||||
|
/**
|
||||||
|
* 导入excel
|
||||||
|
*
|
||||||
|
* @param \app\common\arw\adjfut\src\UploadFile \$file excel
|
||||||
|
*/
|
||||||
|
public static function importExcel(\app\common\arw\adjfut\src\UploadFile \$file): string
|
||||||
|
{
|
||||||
|
\$msg = [];
|
||||||
|
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
\$excel = new Excel(\$file);
|
||||||
|
\$data = \$excel->parseExcel(
|
||||||
|
Tool::getExcelRule(self::EXCELFIELD),
|
||||||
|
[
|
||||||
|
'titleLine' => [1]
|
||||||
|
]);
|
||||||
|
if (!\$data) throwErrorMsg('excel无数据', 1);
|
||||||
|
\$msg = [];
|
||||||
|
foreach (\$data as \$line => \$value) {
|
||||||
|
try {
|
||||||
|
\$model = self::importExcelInit(\$value);
|
||||||
|
\$msg[] = \"{\$line} <span style='color:#27af49'>新增成功!</span><br>\";
|
||||||
|
} catch (\Throwable \$th) {
|
||||||
|
\$msg[] = \"{\$line} <span style='color:red'>{\$th->getMessage()}</span><br>\";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Db::commit();
|
||||||
|
return implode(', ', \$msg);
|
||||||
|
} catch (\Throwable \$th) {
|
||||||
|
Db::rollback();
|
||||||
|
throw \$th;
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建模型层导入初始化方法模板字符
|
||||||
|
*/
|
||||||
|
private function buildImportInitFunTemp(): string
|
||||||
|
{
|
||||||
|
if (!$this->is_import) return '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (匿名函数)获取excel每行导入数据变量分配-模板字符
|
||||||
|
*/
|
||||||
|
$getImportAllocationTemp = function () {
|
||||||
|
$str = "";
|
||||||
|
foreach ($this->business_fields as $field => $name) {
|
||||||
|
$str .= "\${$field} = \$value['{$field}'];";
|
||||||
|
};
|
||||||
|
return $str;
|
||||||
|
};
|
||||||
|
$import_allocation_temp = $getImportAllocationTemp();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (匿名函数)获取新增的字段值们-模板字符
|
||||||
|
*/
|
||||||
|
$getAddFieldsTemp = function () {
|
||||||
|
$str = "";
|
||||||
|
foreach ($this->business_fields as $field => $name) {
|
||||||
|
$str .= "'{$field}' => \${$field},\n";
|
||||||
|
};
|
||||||
|
return $str;
|
||||||
|
};
|
||||||
|
$add_allocation_temp = $getAddFieldsTemp();
|
||||||
|
|
||||||
|
//模板字符构建并返回
|
||||||
|
return "
|
||||||
|
/**
|
||||||
|
* 导入excel初始化
|
||||||
|
*
|
||||||
|
* @param array \$value excel每行数据
|
||||||
|
*/
|
||||||
|
public static function importExcelInit(array \$value):void
|
||||||
|
{
|
||||||
|
{$import_allocation_temp}
|
||||||
|
|
||||||
|
self::create(
|
||||||
|
[{$add_allocation_temp}],
|
||||||
|
{$this->add_fields_temp}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
";
|
||||||
|
}
|
||||||
|
}
|
@ -530,4 +530,22 @@ class Tool
|
|||||||
$model[$field] = $value;
|
$model[$field] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串替换优化版
|
||||||
|
*
|
||||||
|
* @param string $subject 执行替换字符串
|
||||||
|
* @param array $search_and_replace 替换信息 例:[替换目标=>替换值,替换目标=>替换值,...]
|
||||||
|
* @return string 该函数返回替换后的字符串。
|
||||||
|
*/
|
||||||
|
public static function strReplacePlus(string $subject, array $search_and_replace): string
|
||||||
|
{
|
||||||
|
$search = [];
|
||||||
|
$replace = [];
|
||||||
|
foreach ($search_and_replace as $key => $val) {
|
||||||
|
$search[] = $key;
|
||||||
|
$replace[] = $val;
|
||||||
|
};
|
||||||
|
return str_replace($search, $replace, $subject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user