-
Notifications
You must be signed in to change notification settings - Fork 226
feat: 数据范围实现(model use trait实现) #561
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
onekb
wants to merge
1
commit into
mineadmin:master-department
Choose a base branch
from
onekb:master-department-250311
base: master-department
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
| /** | ||
| * This file is part of MineAdmin. | ||
| * | ||
| * @link https://www.mineadmin.com | ||
| * @document https://doc.mineadmin.com | ||
| * @contact root@imoi.cn | ||
| * @license https://github.com/mineadmin/MineAdmin/blob/master/LICENSE | ||
| */ | ||
|
|
||
| namespace App\Model; | ||
|
|
||
| use App\Http\CurrentUser; | ||
| use App\Model\DataPermission\Policy; | ||
| use App\Model\Enums\DataPermission\PolicyType; | ||
| use App\Model\Permission\Department; | ||
| use Hyperf\Database\Model\Builder; | ||
| use Hyperf\Database\Schema\Schema; | ||
|
|
||
| class DataScope | ||
| { | ||
| public static function query(Builder $builder, string $table) | ||
| { | ||
| // 不存在creatd_by字段,则不进行数据过滤 | ||
| if (!Schema::hasColumn($table, 'created_by')) { | ||
| return $builder; | ||
| } | ||
|
|
||
| $currentUser = make(CurrentUser::class); | ||
| try { | ||
| $userId = $currentUser->id(); | ||
| } catch (\Throwable $e) { | ||
| $userId = 0; | ||
| } | ||
|
|
||
| // 只有用户已登录时才应用条件 | ||
| if ($userId) { | ||
| // 超级管理员拥有所有权限,不进行数据过滤 | ||
| if ($currentUser->isSuperAdmin()) { | ||
| return $builder; | ||
| } | ||
|
|
||
| // 获取当前用户 | ||
| $user = $currentUser->user(); | ||
|
|
||
| // 首先获取用户自身的权限策略 | ||
| $userPolicies = Policy::where('user_id', $userId)->get(); | ||
|
|
||
| // 如果用户有自己的权限策略,则只使用用户的策略 | ||
| // 如果用户没有自己的权限策略,才查看角色权限 | ||
| $policies = null; | ||
| if (!$userPolicies->isEmpty()) { | ||
| $policies = $userPolicies->sortByDesc('is_default'); | ||
| } else { | ||
| // 获取用户所有角色ID | ||
| $roleIds = $user->roles()->pluck('role.id')->toArray(); | ||
| $rolePolicies = empty($roleIds) ? collect() : Policy::whereIn('role_id', $roleIds)->get(); | ||
| $policies = $rolePolicies->sortByDesc('is_default'); | ||
| } | ||
|
|
||
| // 如果没有任何策略,默认使用"仅自己"权限 | ||
| if ($policies->isEmpty()) { | ||
| return $builder->where("{$table}.created_by", $userId); | ||
| } | ||
|
|
||
| // 处理策略:如果任一策略是ALL,则不限制访问范围 | ||
| if ($policies->contains('policy_type', PolicyType::All)) { | ||
| return $builder; | ||
| } | ||
|
|
||
| // 收集所有可访问的部门ID和用户ID | ||
| $deptIds = collect(); | ||
| $createdByIds = collect([$userId]); // 默认自己的数据总是可访问的 | ||
|
|
||
| // 用户所有的部门ID(处理多部门情况) | ||
| $userDeptIds = $user->department()->pluck('id')->toArray(); | ||
|
|
||
| // 处理每个策略 | ||
| foreach ($policies as $policy) { | ||
| switch ($policy->policy_type) { | ||
| case PolicyType::Self: | ||
| // 仅自己 - 已包含在默认的createdByIds中 | ||
| break; | ||
|
|
||
| case PolicyType::DeptSelf: | ||
| // 本部门数据 - 将用户所有部门添加到可访问部门列表 | ||
| $deptIds = $deptIds->merge($userDeptIds); | ||
| break; | ||
|
|
||
| case PolicyType::DeptTree: | ||
| // 本部门及以下数据 | ||
| if (!empty($userDeptIds)) { | ||
| $allDeptIds = []; | ||
| // 获取每个部门的子部门 | ||
| foreach ($userDeptIds as $deptId) { | ||
| $allDeptIds[] = $deptId; | ||
| self::getChildDeptIds($deptId, $allDeptIds); | ||
| } | ||
| $deptIds = $deptIds->merge($allDeptIds); | ||
| } | ||
| break; | ||
|
|
||
| case PolicyType::CustomDept: | ||
| // 自定义部门 | ||
| $customDeptIds = $policy->value['dept_ids'] ?? []; | ||
| if (!empty($customDeptIds)) { | ||
| $deptIds = $deptIds->merge($customDeptIds); | ||
| } | ||
| break; | ||
|
|
||
| case PolicyType::CustomFunc: | ||
| // 自定义函数 | ||
| $funcName = $policy->value['func_name'] ?? ''; | ||
| if (!empty($funcName) && function_exists($funcName)) { | ||
| // 这里需要特殊处理,因为自定义函数可能返回复杂的查询条件 | ||
| // 临时保存在一个独立的变量里,后面再合并 | ||
| $customQuery = (clone $builder); | ||
| $customQuery = call_user_func($funcName, $customQuery, $table, $userId, $user); | ||
|
|
||
| // TODO: 这里需要一个额外的机制来合并自定义查询的结果 | ||
| // 由于无法直接合并查询条件,需要通过其他方式实现 | ||
|
|
||
| // 这里是一个简单的处理方案,如果有自定义函数,我们暂时将其作为一个单独的Or条件 | ||
| // 真实场景中可能需要更复杂的合并逻辑 | ||
| return $builder->where(function ($query) use ($table, $createdByIds, $deptIds, $customQuery) { | ||
| // 应用其他范围条件 | ||
| if (!$deptIds->isEmpty()) { | ||
| $query->orWhereIn("{$table}.dept_id", $deptIds->unique()->values()->toArray()); | ||
| } | ||
| if (!$createdByIds->isEmpty()) { | ||
| $query->orWhereIn("{$table}.created_by", $createdByIds->unique()->values()->toArray()); | ||
| } | ||
|
|
||
| // 添加自定义查询的条件(简化处理,实际可能需要更复杂的逻辑) | ||
| $query->orWhereRaw('EXISTS (' . $customQuery->toSql() . ')', $customQuery->getBindings()); | ||
| }); | ||
| } | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| // 根据收集到的部门ID和用户ID构建最终的查询条件 | ||
| return $builder->where(function ($query) use ($table, $createdByIds, $deptIds) { | ||
| // 如果有部门ID,添加部门条件 | ||
| if (!$deptIds->isEmpty()) { | ||
| $query->orWhereIn("{$table}.dept_id", $deptIds->unique()->values()->toArray()); | ||
| } | ||
|
|
||
| // 添加创建者条件 | ||
| if (!$createdByIds->isEmpty()) { | ||
| $query->orWhereIn("{$table}.created_by", $createdByIds->unique()->values()->toArray()); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| return $builder; | ||
| } | ||
|
|
||
| /** | ||
| * 递归获取子部门ID | ||
| */ | ||
| private static function getChildDeptIds(int $deptId, array &$deptIds): void | ||
| { | ||
| $childDepts = Department::where('parent_id', $deptId)->get(); | ||
|
|
||
| foreach ($childDepts as $dept) { | ||
| $deptIds[] = $dept->id; | ||
| self::getChildDeptIds($dept->id, $deptIds); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
| /** | ||
| * This file is part of MineAdmin. | ||
| * | ||
| * @link https://www.mineadmin.com | ||
| * @document https://doc.mineadmin.com | ||
| * @contact root@imoi.cn | ||
| * @license https://github.com/mineadmin/MineAdmin/blob/master/LICENSE | ||
| */ | ||
|
|
||
| namespace App\Model\Scopes; | ||
|
|
||
| use App\Http\CurrentUser; | ||
| use App\Model\DataPermission\Policy; | ||
| use App\Model\DataScope; | ||
| use App\Model\Enums\DataPermission\PolicyType; | ||
| use App\Model\Permission\Department; | ||
| use Hyperf\Database\Model\Builder; | ||
| use Hyperf\Database\Model\Model; | ||
| use Hyperf\Database\Model\Scope; | ||
| use Hyperf\Database\Schema\Schema; | ||
|
|
||
| class DataScopeScope implements Scope | ||
| { | ||
| /** | ||
| * 应用作用域 | ||
| */ | ||
| public function apply(Builder $builder, Model $model): void | ||
| { | ||
| $table = $model->getTable(); | ||
|
|
||
| if ($model->getDataScope()) { | ||
| DataScope::query($builder, $table); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
| /** | ||
| * This file is part of MineAdmin. | ||
| * | ||
| * @link https://www.mineadmin.com | ||
| * @document https://doc.mineadmin.com | ||
| * @contact root@imoi.cn | ||
| * @license https://github.com/mineadmin/MineAdmin/blob/master/LICENSE | ||
| */ | ||
|
|
||
| namespace App\Model\Traits; | ||
|
|
||
| trait DataScopeTrait | ||
| { | ||
| public $dataScope = false; | ||
|
|
||
| protected function boot(): void | ||
| { | ||
| parent::boot(); | ||
|
|
||
| static::addGlobalScope(make(\App\Model\Scopes\DataScopeScope::class)); | ||
| } | ||
|
|
||
| public function getDataScope() | ||
| { | ||
| return $this->dataScope; | ||
| } | ||
| public function setDataScope($dataScope) | ||
| { | ||
| $this->dataScope = $dataScope; | ||
| return $this; | ||
| } | ||
|
|
||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
自定义查询合并方式过于简化,可能影响数据范围准确性
在这里通过
orWhereRaw('EXISTS (...)')与自定义查询合并,实际场景或复杂度更高时,可能导致数据范围不一致或性能问题。建议采用更完善的处理方案并进行验证,以确保不同策略能合理组合。