初始化

This commit is contained in:
2026-03-09 17:35:53 +08:00
commit 74f322b7c2
577 changed files with 57404 additions and 0 deletions

72
app/admin/model/Admin.php Normal file
View File

@@ -0,0 +1,72 @@
<?php
namespace app\admin\model;
use think\Model;
use think\facade\Db;
/**
* Admin模型
* @property int $id 管理员ID
* @property string $username 管理员用户名
* @property string $nickname 管理员昵称
* @property string $email 管理员邮箱
* @property string $mobile 管理员手机号
* @property string $last_login_ip 上次登录IP
* @property string $last_login_time 上次登录时间
* @property int $login_failure 登录失败次数
* @property string $password 密码密文
* @property string $salt 密码盐(废弃待删)
* @property string $status 状态:enable=启用,disable=禁用,...(string存储可自定义其他)
*/
class Admin extends Model
{
/**
* @var string 自动写入时间戳
*/
protected $autoWriteTimestamp = true;
/**
* 追加属性
*/
protected $append = [
'group_arr',
'group_name_arr',
];
public function getGroupArrAttr($value, $row): array
{
return Db::name('admin_group_access')
->where('uid', $row['id'])
->column('group_id');
}
public function getGroupNameArrAttr($value, $row): array
{
$groupAccess = Db::name('admin_group_access')
->where('uid', $row['id'])
->column('group_id');
return AdminGroup::whereIn('id', $groupAccess)->column('name');
}
public function getAvatarAttr($value): string
{
return full_url($value, false, config('buildadmin.default_avatar'));
}
public function setAvatarAttr($value): string
{
return $value == full_url('', false, config('buildadmin.default_avatar')) ? '' : $value;
}
/**
* 重置用户密码
* @param int|string $uid 管理员ID
* @param string $newPassword 新密码
* @return int|Admin
*/
public function resetPassword(int|string $uid, string $newPassword): int|Admin
{
return $this->where(['id' => $uid])->update(['password' => hash_password($newPassword), 'salt' => '']);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace app\admin\model;
use think\Model;
/**
* AdminGroup模型
*/
class AdminGroup extends Model
{
protected $autoWriteTimestamp = true;
}

View File

@@ -0,0 +1,160 @@
<?php
namespace app\admin\model;
use Throwable;
use think\Model;
use app\admin\library\Auth;
use think\model\relation\BelongsTo;
/**
* AdminLog模型
*/
class AdminLog extends Model
{
protected $autoWriteTimestamp = true;
protected $updateTime = false;
/**
* 自定义日志标题
* @var string
*/
protected string $title = '';
/**
* 自定义日志内容
* @var string|array
*/
protected string|array $data = '';
/**
* 忽略的链接正则列表
* @var array
*/
protected array $urlIgnoreRegex = [
'/^(.*)\/(select|index|logout)$/i',
];
protected array $desensitizationRegex = [
'/(password|salt|token)/i'
];
public static function instance()
{
$request = request();
if (!isset($request->adminLog)) {
$request->adminLog = new static();
}
return $request->adminLog;
}
/**
* 设置标题
* @param string $title
*/
public function setTitle(string $title): void
{
$this->title = $title;
}
/**
* 设置日志内容
* @param string|array $data
*/
public function setData(string|array $data): void
{
$this->data = $data;
}
/**
* 设置忽略的链接正则列表
* @param array|string $regex
*/
public function setUrlIgnoreRegex(array|string $regex = []): void
{
$regex = is_array($regex) ? $regex : [$regex];
$this->urlIgnoreRegex = array_merge($this->urlIgnoreRegex, $regex);
}
/**
* 设置需要进行数据脱敏的正则列表
* @param array|string $regex
*/
public function setDesensitizationRegex(array|string $regex = []): void
{
$regex = is_array($regex) ? $regex : [$regex];
$this->desensitizationRegex = array_merge($this->desensitizationRegex, $regex);
}
/**
* 数据脱敏(只数组,根据数组 key 脱敏)
* @param array|string $data
* @return array|string
*/
protected function desensitization(array|string $data): array|string
{
if (!is_array($data) || !$this->desensitizationRegex) {
return $data;
}
foreach ($data as $index => &$item) {
foreach ($this->desensitizationRegex as $reg) {
if (preg_match($reg, $index)) {
$item = "***";
} elseif (is_array($item)) {
$item = $this->desensitization($item);
}
}
}
return $data;
}
/**
* 写入日志
* @param string $title
* @param string|array|null $data
* @throws Throwable
*/
public function record(string $title = '', string|array|null $data = null): void
{
$auth = Auth::instance();
$adminId = $auth->isLogin() ? $auth->id : 0;
$username = $auth->isLogin() ? $auth->username : request()->param('username', __('Unknown'));
$controller = str_replace('.', '/', request()->controller(true));
$action = request()->action(true);
$path = $controller . '/' . $action;
if ($this->urlIgnoreRegex) {
foreach ($this->urlIgnoreRegex as $item) {
if (preg_match($item, $path)) {
return;
}
}
}
$data = $data ?: $this->data;
if (!$data) {
$data = request()->param('', null, 'trim,strip_tags,htmlspecialchars');
}
$data = $this->desensitization($data);
$title = $title ?: $this->title;
if (!$title) {
$controllerTitle = AdminRule::where('name', $controller)->value('title');
$title = AdminRule::where('name', $path)->value('title');
$title = $title ?: __('Unknown') . '(' . $action . ')';
$title = $controllerTitle ? ($controllerTitle . '-' . $title) : $title;
}
self::create([
'admin_id' => $adminId,
'username' => $username,
'url' => substr(request()->url(), 0, 1500),
'title' => $title,
'data' => !is_scalar($data) ? json_encode($data) : $data,
'ip' => request()->ip(),
'useragent' => substr(request()->server('HTTP_USER_AGENT'), 0, 255),
]);
}
public function admin(): BelongsTo
{
return $this->belongsTo(Admin::class);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace app\admin\model;
use think\Model;
/**
* AdminRule 模型
* @property int $status 状态:0=禁用,1=启用
*/
class AdminRule extends Model
{
protected $autoWriteTimestamp = true;
public function setComponentAttr($value)
{
if ($value) $value = str_replace('\\', '/', $value);
return $value;
}
}

133
app/admin/model/Config.php Normal file
View File

@@ -0,0 +1,133 @@
<?php
namespace app\admin\model;
use Throwable;
use think\Model;
use think\facade\Cache;
/**
* 系统配置模型
* @property mixed $content
* @property mixed $rule
* @property mixed $extend
* @property mixed $allow_del
*/
class Config extends Model
{
public static string $cacheTag = 'sys_config';
protected $append = [
'value',
'content',
'extend',
'input_extend',
];
protected array $jsonDecodeType = ['checkbox', 'array', 'selects'];
protected array $needContent = ['radio', 'checkbox', 'select', 'selects'];
/**
* 入库前
* @throws Throwable
*/
public static function onBeforeInsert(Config $model): void
{
if (!in_array($model->getData('type'), $model->needContent)) {
$model->content = null;
} else {
$model->content = json_encode(str_attr_to_array($model->getData('content')));
}
if (is_array($model->rule)) {
$model->rule = implode(',', $model->rule);
}
if ($model->getData('extend') || $model->getData('inputExtend')) {
$extend = str_attr_to_array($model->getData('extend'));
$inputExtend = str_attr_to_array($model->getData('inputExtend'));
if ($inputExtend) $extend['baInputExtend'] = $inputExtend;
if ($extend) $model->extend = json_encode($extend);
}
$model->allow_del = 1;
}
/**
* 写入后
*/
public static function onAfterWrite(): void
{
// 清理配置缓存
Cache::tag(self::$cacheTag)->clear();
}
public function getValueAttr($value, $row)
{
if (!isset($row['type']) || $value == '0') return $value;
if (in_array($row['type'], $this->jsonDecodeType)) {
return empty($value) ? [] : json_decode($value, true);
} elseif ($row['type'] == 'switch') {
return (bool)$value;
} elseif ($row['type'] == 'editor') {
return !$value ? '' : htmlspecialchars_decode($value);
} elseif (in_array($row['type'], ['city', 'remoteSelects'])) {
if (!$value) return [];
if (!is_array($value)) return explode(',', $value);
return $value;
} else {
return $value ?: '';
}
}
public function setValueAttr(mixed $value, $row): mixed
{
if (in_array($row['type'], $this->jsonDecodeType)) {
return $value ? json_encode($value) : '';
} elseif ($row['type'] == 'switch') {
return $value ? '1' : '0';
} elseif ($row['type'] == 'time') {
return $value ? date('H:i:s', strtotime($value)) : '';
} elseif ($row['type'] == 'city') {
if ($value && is_array($value)) {
return implode(',', $value);
}
return $value ?: '';
} elseif (is_array($value)) {
return implode(',', $value);
}
return $value;
}
public function getContentAttr($value, $row)
{
if (!isset($row['type'])) return '';
if (in_array($row['type'], $this->needContent)) {
$arr = json_decode($value, true);
return $arr ?: [];
} else {
return '';
}
}
public function getExtendAttr($value)
{
if ($value) {
$arr = json_decode($value, true);
if ($arr) {
unset($arr['baInputExtend']);
return $arr;
}
}
return [];
}
public function getInputExtendAttr($value, $row)
{
if ($row && $row['extend']) {
$arr = json_decode($row['extend'], true);
if ($arr && isset($arr['baInputExtend'])) {
return $arr['baInputExtend'];
}
}
return [];
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace app\admin\model;
use think\Model;
/**
* Log
*/
class CrudLog extends Model
{
// 表名
protected $name = 'crud_log';
// 自动写入时间戳字段
protected $autoWriteTimestamp = true;
protected $updateTime = false;
protected $type = [
'table' => 'array',
'fields' => 'array',
];
}

View File

@@ -0,0 +1,15 @@
<?php
namespace app\admin\model;
use think\Model;
/**
* DataRecycle 模型
*/
class DataRecycle extends Model
{
protected $name = 'security_data_recycle';
protected $autoWriteTimestamp = true;
}

View File

@@ -0,0 +1,27 @@
<?php
namespace app\admin\model;
use think\Model;
use think\model\relation\BelongsTo;
/**
* DataRecycleLog 模型
*/
class DataRecycleLog extends Model
{
protected $name = 'security_data_recycle_log';
protected $autoWriteTimestamp = true;
protected $updateTime = false;
public function recycle(): BelongsTo
{
return $this->belongsTo(DataRecycle::class, 'recycle_id');
}
public function admin(): BelongsTo
{
return $this->belongsTo(Admin::class, 'admin_id');
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace app\admin\model;
use think\Model;
/**
* SensitiveData 模型
*/
class SensitiveData extends Model
{
protected $name = 'security_sensitive_data';
protected $autoWriteTimestamp = true;
protected $type = [
'data_fields' => 'array',
];
}

View File

@@ -0,0 +1,27 @@
<?php
namespace app\admin\model;
use think\Model;
use think\model\relation\BelongsTo;
/**
* SensitiveDataLog 模型
*/
class SensitiveDataLog extends Model
{
protected $name = 'security_sensitive_data_log';
protected $autoWriteTimestamp = true;
protected $updateTime = false;
public function sensitive(): BelongsTo
{
return $this->belongsTo(SensitiveData::class, 'sensitive_id');
}
public function admin(): BelongsTo
{
return $this->belongsTo(Admin::class, 'admin_id');
}
}

52
app/admin/model/User.php Normal file
View File

@@ -0,0 +1,52 @@
<?php
namespace app\admin\model;
use think\Model;
use think\model\relation\BelongsTo;
/**
* User 模型
* @property int $id 用户ID
* @property string password 密码密文
*/
class User extends Model
{
protected $autoWriteTimestamp = true;
public function getAvatarAttr($value): string
{
return full_url($value, false, config('buildadmin.default_avatar'));
}
public function setAvatarAttr($value): string
{
return $value == full_url('', false, config('buildadmin.default_avatar')) ? '' : $value;
}
public function getMoneyAttr($value): string
{
return bcdiv($value, 100, 2);
}
public function setMoneyAttr($value): string
{
return bcmul($value, 100, 2);
}
public function userGroup(): BelongsTo
{
return $this->belongsTo(UserGroup::class, 'group_id');
}
/**
* 重置用户密码
* @param int|string $uid 用户ID
* @param string $newPassword 新密码
* @return int|User
*/
public function resetPassword(int|string $uid, string $newPassword): int|User
{
return $this->where(['id' => $uid])->update(['password' => hash_password($newPassword), 'salt' => '']);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace app\admin\model;
use think\Model;
/**
* UserGroup 模型
*/
class UserGroup extends Model
{
protected $autoWriteTimestamp = true;
}

View File

@@ -0,0 +1,80 @@
<?php
namespace app\admin\model;
use Throwable;
use think\model;
use think\Exception;
use think\model\relation\BelongsTo;
/**
* UserMoneyLog 模型
* 1. 创建余额日志自动完成会员余额的添加
* 2. 创建余额日志时,请开启事务
*/
class UserMoneyLog extends model
{
protected $autoWriteTimestamp = true;
protected $updateTime = false;
/**
* 入库前
* @throws Throwable
*/
public static function onBeforeInsert($model): void
{
$user = User::where('id', $model->user_id)->lock(true)->find();
if (!$user) {
throw new Exception("The user can't find it");
}
if (!$model->memo) {
throw new Exception("Change note cannot be blank");
}
$model->before = $user->money;
$user->money += $model->money;
$user->save();
$model->after = $user->money;
}
public static function onBeforeDelete(): bool
{
return false;
}
public function getMoneyAttr($value): string
{
return bcdiv($value, 100, 2);
}
public function setMoneyAttr($value): string
{
return bcmul($value, 100, 2);
}
public function getBeforeAttr($value): string
{
return bcdiv($value, 100, 2);
}
public function setBeforeAttr($value): string
{
return bcmul($value, 100, 2);
}
public function getAfterAttr($value): string
{
return bcdiv($value, 100, 2);
}
public function setAfterAttr($value): string
{
return bcmul($value, 100, 2);
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace app\admin\model;
use think\model;
/**
* UserRule 模型
* @property int $status 状态:0=禁用,1=启用
*/
class UserRule extends model
{
protected $autoWriteTimestamp = true;
protected static function onAfterInsert($model): void
{
$pk = $model->getPk();
$model->where($pk, $model[$pk])->update(['weigh' => $model[$pk]]);
}
public function setComponentAttr($value)
{
if ($value) $value = str_replace('\\', '/', $value);
return $value;
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace app\admin\model;
use Throwable;
use think\model;
use think\Exception;
use think\model\relation\BelongsTo;
/**
* UserScoreLog 模型
* 1. 创建积分日志自动完成会员积分的添加
* 2. 创建积分日志时,请开启事务
*/
class UserScoreLog extends model
{
protected $autoWriteTimestamp = true;
protected $updateTime = false;
/**
* 入库前
* @throws Throwable
*/
public static function onBeforeInsert($model): void
{
$user = User::where('id', $model->user_id)->lock(true)->find();
if (!$user) {
throw new Exception("The user can't find it");
}
if (!$model->memo) {
throw new Exception("Change note cannot be blank");
}
$model->before = $user->score;
$user->score += $model->score;
$user->save();
$model->after = $user->score;
}
public static function onBeforeDelete(): bool
{
return false;
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
}