| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- <?php
- // +----------------------------------------------------------------------
- // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
- // +----------------------------------------------------------------------
- // | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
- // +----------------------------------------------------------------------
- // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
- // +----------------------------------------------------------------------
- // | Author: CRMEB Team <admin@crmeb.com>
- // +----------------------------------------------------------------------
- namespace app\common\repositories\user;
- use app\common\dao\user\UserInfoDao;
- use app\common\dao\user\UserSignDao;
- use app\common\repositories\BaseRepository;
- use app\common\repositories\system\config\ConfigValueRepository;
- use FormBuilder\Factory\Elm;
- use Swoole\Database\MysqliException;
- use think\exception\ValidateException;
- use think\facade\Db;
- use think\facade\Route;
- /**
- * @mixin UserInfoDao
- */
- class UserInfoRepository extends BaseRepository
- {
- protected $type = [
- 'input' => ['label' => '文本', 'type' => 'varchar(255)', 'value' => '', 'default' => ''],
- 'int' => ['label' => '数字', 'type' => 'int(11)', 'value' => 0, 'default' => 0],
- 'phone' => ['label' => '手机号', 'type' => 'varchar(11)', 'value' => '', 'default' => ''],
- 'date' => ['label' => '时间', 'type' => 'date', 'value' => null,'default' => 'NULL'],
- 'radio' => ['label' => '单选', 'type' => 'tinyint(1)', 'value' => 0, 'default' => 0],
- 'address' => ['label' => '地址', 'type' => 'varchar(255)', 'value' => '', 'default' => ''],
- 'id_card' => ['label' => '身份证', 'type' => 'varchar(255)', 'value' => '', 'default' => ''],
- 'email' => ['label' => '邮箱', 'type' => 'varchar(255)', 'value' => '', 'default' => ''],
- ];
- const FIXED_FIELD = ['uid'];
- /**
- * @var UserInfoDao
- */
- protected $dao;
- /**
- * UserSignRepository constructor.
- * @param UserInfoDao $dao
- */
- public function __construct(UserInfoDao $dao)
- {
- $this->dao = $dao;
- }
- // 验证是否是有效的列名
- /**
- * 检查列名是否有效。
- *
- * 该方法用于验证传入的列名是否符合规定的格式。列名必须以字母开头,后续可以是字母、数字或下划线的组合。
- * 如果列名不符合这个规则,将抛出一个ValidateException异常,指出列名无效。
- *
- * @param string $columnName 待验证的列名。
- * @throws ValidateException 如果列名无效,则抛出此异常。
- */
- protected function isValidColumnName($columnName)
- {
- // 使用正则表达式检查列名是否以字母开头,且仅包含字母、数字和下划线
- // 检查是否以字母开头,只包含字母、数字和下划线
- if (!preg_match('/^[a-z][a-z0-9_]*$/', $columnName)) {
- throw new ValidateException("无效的列名: {$columnName}");
- }
- }
- /**
- * 执行SQL语句。
- *
- * 本函数用于执行给定的SQL语句,通过传递数据库连接和SQL语句参数来完成。
- * 如果在执行SQL语句时遇到MysqliException异常,它将被捕获并重新抛出为ValidateException异常。
- * 这种异常处理机制允许上层调用者更统一地处理异常情况,尤其是数据库操作相关的异常。
- *
- * @param $db PDO 实例,用于执行SQL语句的数据库连接。
- * @param $sql string,待执行的SQL语句。
- * @param $params array,默认为空数组,包含SQL语句中需要的参数。
- * @throws ValidateException 如果执行SQL语句时发生错误,则抛出此异常。
- */
- public function executeSql($db, $sql, $params = [])
- {
- try {
- $db->execute($sql, $params);
- } catch (MysqliException $exception) {
- throw new ValidateException($exception->getMessage());
- }
- }
- /**
- * 创建字段
- * @param array $data
- * @param $type
- * @return bool
- *
- * @date 2023/09/25
- * @author yyw
- */
- protected function changeField(array $data, $type = true)
- {
- // 获取数据库连接实例
- $db = Db::connect();
- //操作的数据表
- $tableName = env('database.prefix', 'eb_') . 'user_fields';
- // 操作的字段名称
- $fieldName = $data['field'];
- // 验证字段端名是否合法
- $this->isValidColumnName($fieldName);
- //
- if ($type) {
- // 字段类型,可以根据需要进行修改
- $fieldType = $this->type[$data['type']]['type'] ?? 'varchar(255)';
- $comment = $data['title'] ?: $data['msg'];
- $default = $this->type[$data['type']]['default'];
- $sql = "ALTER TABLE `$tableName` ADD COLUMN `$fieldName` $fieldType NULL DEFAULT '$default' COMMENT '$comment'";
- } else {
- $sql = "ALTER TABLE `$tableName` DROP COLUMN `$fieldName`";
- }
- $this->executeSql($db, $sql);
- return true;
- }
- /**
- * 删除字段
- * @param string $fieldName
- * @return bool
- *
- * @date 2023/09/25
- * @author yyw
- */
- protected function deleteField(string $fieldName)
- {
- // 验证字段名称是否有效
- $this->isValidColumnName($fieldName);
- // 获取数据库连接实例
- $db = Db::connect();
- // 操作的数据表
- $tableName = env('database.prefix', 'eb_') . 'user_fields';
- // 构建 SQL 删除字段的语句
- $sql = "ALTER TABLE `$tableName` DROP COLUMN `$fieldName`";
- $this->executeSql($db, $sql);
- return true;
- }
- /**
- * 获取列表数据
- * 根据给定的条件和分页参数,从数据库中获取列表数据,并进行相关处理。
- *
- * @param array $where 查询条件,用于筛选数据。
- * @param int $page 当前页码,用于分页查询。
- * @param int $limit 每页数据条数,用于分页查询。
- * @return array 返回包含数据总数、数据列表和默认头像信息的数组。
- */
- public function getList(array $where = [], int $page = 1, int $limit = 10)
- {
- // 根据条件获取查询对象
- $query = $this->dao->getSearch($where);
- // 计算满足条件的数据总数
- $count = $query->count();
- // 获取排序后的数据列表
- $list = $query->order(['sort', 'create_time' => 'ASC'])->select()->each(function ($item) {
- // 给数据项的type_name赋值,来源于type数组中对应type的label值
- return $item->type_name = $this->type[$item->type]['label'];
- });
- // 获取系统配置的默认用户头像地址
- $avatar = systemConfig('user_default_avatar');
- // 返回包含数据总数、处理后的数据列表和默认头像信息的数组
- return compact('count', 'list', 'avatar');
- }
- /**
- * 创建表单用于添加字段信息
- *
- * 该方法通过Element UI的表单构建器创建一个表单,用于添加系统用户信息字段。
- * 表单包括字段类型的选择、字段的名称、键值和提示信息的输入。
- * 其中字段类型为单选框,选项通过getType方法获取,当选择字段类型为'radio'时,需要额外输入配置内容。
- *
- * @return \Encore\Admin\Widgets\Form|\FormBuilder\Form
- */
- public function createFrom()
- {
- // 构建表单提交的URL
- $action = Route::buildUrl('systemUserInfoCreate')->build();
- // 使用Elm表单构建器创建表单,并设置表单字段
- $form = Elm::createForm($action, [
- // 字段类型选择器,包括获取字段类型的选项和针对'radio'类型字段的额外配置输入
- Elm::select('type', '字段类型:')->options($this->getType())->control([
- [
- 'value' => 'radio',
- 'rule' => [
- Elm::textarea('content', '配置内容')->placeholder('请输入配置内容')->required()
- ]
- ]
- ])->required(),
- // 字段名称输入框
- Elm::input('title', '字段名称:')->placeholder('请输入字段名称')->required(),
- // 字段键值输入框
- Elm::input('field', '字段key:')->placeholder('请输入字段key')->required(),
- // 提示信息输入框
- Elm::input('msg', '提示信息:')->placeholder('请输入提示信息')->required()
- ]);
- // 设置表单标题
- return $form->setTitle('添加字段');
- }
- /**
- * 创建字段
- *
- * 本函数用于根据传入的数据创建新的字段。它首先验证字段类型是否有效,然后确保对于单选字段类型(radio),
- * 至少提供了两个选项。如果验证失败,将抛出一个验证异常。然后,它将内容数据编码为JSON格式,并为字段设置默认的排序值。
- * 最后,它在数据库事务中执行字段的创建操作。
- *
- * @param array $data 包含字段相关信息的数组,包括字段类型、内容等。
- * @return bool|void 返回数据库事务执行结果,如果执行成功,则返回true,否则不返回任何值。
- * @throws ValidateException 如果字段类型无效或单选字段选项不足两个,将抛出此异常。
- */
- public function create(array $data)
- {
- // 验证字段类型是否在定义的类型数组中
- // 验证类型
- if (!isset($this->type[$data['type']])) {
- throw new ValidateException('字段类型异常');
- }
- // 对于单选字段类型(radio),确保至少有两个选项
- if ($data['type'] == 'radio' && (!is_array($data['content']) || count($data['content']) < 2)) {
- throw new ValidateException('请至少创建两个选项');
- }
- // 将内容数据编码为JSON格式
- $data['content'] = json_encode($data['content']);
- // 设置默认的排序值
- $data['sort'] = 999;
- // 使用数据库事务执行字段的创建操作,包括更改字段信息和实际创建字段的操作
- return Db::transaction(function () use ($data) {
- $this->changeField($data);
- $this->dao->create($data);
- });
- }
- /**
- * 保存用户信息及扩展字段配置
- *
- * 本函数主要用于处理用户信息的保存操作,其中包括用户头像信息的更新,
- * 以及用户扩展信息字段的配置更新。通过对传入数据的处理,确保了用户
- * 信息的完整性和扩展字段的正确配置。
- *
- * @param array $data 用户信息及扩展字段数据数组
- * 包含 'avatar' 用户头像信息
- * 包含 'user_extend_info' 用户扩展信息字段数组
- * @return bool 返回保存操作的结果,true表示保存成功
- */
- public function saveAll(array $data = [])
- {
- // 设置用户默认头像信息
- app()->make(ConfigValueRepository::class)->setFormData(['user_default_avatar' => $data['avatar']], 0);
- // 检查并更新用户扩展信息字段的配置
- // 保存用户表单
- if (!empty($data['user_extend_info'])) {
- foreach ($data['user_extend_info'] as $sort => $item) {
- // 确保必要参数存在,避免空值导致的错误
- if (!isset($item['field']) || !isset($item['is_used']) || !isset($item['is_require']) || !isset($item['is_show'])) {
- throw new ValidateException('参数不能为空');
- }
- // 确保参数值的合法性,只允许特定的取值范围
- if (!in_array($item['is_used'], [0, 1]) || !in_array($item['is_require'], [0, 1]) || !in_array($item['is_show'], [0, 1])) {
- throw new ValidateException('参数类性错误');
- }
- // 更新扩展字段的配置信息
- $this->dao->query([])->where('field', $item['field'])->update(['is_used' => $item['is_used'], 'is_require' => $item['is_require'], 'is_show' => $item['is_show'], 'sort' => $sort]);
- }
- }
- return true;
- }
- /**
- * 删除表单数据。
- *
- * 本函数用于根据给定的ID删除表单数据。在删除前,它会进行一系列的验证以确保数据的安全性和完整性。
- * 首先,它会检查所要删除的数据是否存在以及'field'字段是否为空,如果存在异常情况,则会抛出一个验证异常。
- * 其次,它会检查待删除的数据是否为默认数据,如果是默认数据,则同样会抛出一个验证异常,以防止删除重要的默认设置。
- * 最后,如果所有验证通过,函数将使用事务来确保删除操作的原子性,即先删除字段数据,再删除实际的表单数据。
- *
- * @param int $id 表单数据的唯一标识ID。
- * @return bool 如果删除成功,返回true。
- * @throws ValidateException 如果数据不存在、字段为空或尝试删除默认表单数据,则抛出此异常。
- */
- public function delete(int $id)
- {
- // 通过ID获取表单信息
- $info = $this->dao->get($id);
- // 检查获取的表单信息是否为空,或者字段是否为空,如果是,则抛出异常
- if (empty($info) || empty($info['field'])) {
- throw new ValidateException('表单数据异常');
- }
- // 检查是否为默认表单,如果是,则抛出异常,防止删除默认表单
- if ($info['is_default']) {
- throw new ValidateException('默认表单不能删除');
- }
- // 使用事务来确保删除操作的原子性
- return Db::transaction(function () use ($info, $id) {
- // 删除字段数据
- $this->deleteField($info['field']);
- // 删除实际的表单数据
- $this->dao->delete($id);
- });
- }
- /**
- * 获取类型信息
- *
- * 本方法旨在通过转换内部类型数组,返回一个格式化后的类型信息数组。每个类型信息包括
- * 值和描述,其中值对应于类型标识,描述则来自于类型数组中的相应元素。
- *
- * @return array 返回一个格式化后的类型信息数组,每个元素包含 'value' 和 'desc' 两个键,
- * 'value' 表示类型的标识,'desc' 是对应的类型描述。
- */
- public function getType()
- {
- // 初始化一个空数组,用于存放转换后的类型信息
- $res = [];
- // 遍历内部类型数组,对每个类型进行处理
- foreach ($this->type as $k => $v) {
- // 将类型标识作为值赋给当前类型信息
- $v['value'] = $k;
- // 移除原始类型数组中的'type'键,避免冗余信息
- unset($v['type']);
- // 将处理后的类型信息添加到结果数组中
- $res[] = $v;
- }
- // 返回转换后的类型信息数组
- return $res;
- }
- /**
- * 获取选择列表
- *
- * 本函数用于检索数据库中特定条件下的数据,这些数据主要用于生成前端的下拉选择列表。
- * 具体来说,它查询了被标记为已使用且类型不为'radio'或'date'的条目,并按排序和创建时间升序返回它们的标题和字段值。
- *
- * @return array 返回一个数组,其中每个元素包含标签(label)和值(value),用于生成选择列表。
- */
- public function getSelectList()
- {
- // 使用DAO对象进行数据库查询,设置条件以获取已使用且类型不是'radio'或'date'的记录
- // 排序方式为按排序字段和创建时间升序
- // 最后,只返回标题作为标签和字段作为值的列
- return $this->dao->getSearch(['is_used' => 1])
- ->whereNotIn('type', ['radio', 'date'])
- ->order(['sort', 'create_time' => 'ASC'])
- ->column('title as label,field as value');
- }
- /**
- * 验证字段是否为默认字段
- * @param string $fields
- * @return bool
- */
- public function getFieldsIsItDefault(string $field)
- {
- return (bool)$this->getSearch(['field' => $field])->value('is_default', 0);
- }
- }
|