Files
lotteryLaravel/app/Support/AgentDelegationAuthorization.php
kang 0841fbed32 feat: 增强管理员功能与数据处理
- 在多个控制器中引入 agent_node_id,以支持基于代理节点的权限和数据过滤。
- 更新 AdminRole 和 AdminUser 模型,新增角色范围和代理节点相关功能,提升角色管理的灵活性。
- 在请求验证中添加 agent_node_id 字段,确保 API 接口支持代理节点的相关操作。
- 优化 LotterySettings 服务,支持批量写入设置,提升配置管理的效率。
- 更新仪表板和报告服务,增强数据统计功能,确保管理员能够获取更全面的统计信息。
2026-06-02 14:36:58 +08:00

152 lines
4.8 KiB
PHP

<?php
namespace App\Support;
use App\Models\AdminUser;
use App\Models\AgentNode;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
final class AgentDelegationAuthorization
{
/**
* @return list<string> menu_action.permission_code
*/
public static function delegationMenuActionCodesForAgent(AgentNode $agent): array
{
if ($agent->isRoot()) {
return DB::table('admin_menu_actions')->where('status', 1)->pluck('permission_code')->all();
}
return DB::table('agent_delegation_grants as g')
->join('admin_menu_actions as ma', 'ma.id', '=', 'g.menu_action_id')
->where('g.child_agent_id', $agent->id)
->where('ma.status', 1)
->pluck('ma.permission_code')
->all();
}
/**
* @return list<string> prd.*
*/
public static function delegationLegacySlugsForAgent(AgentNode $agent): array
{
$codes = self::delegationMenuActionCodesForAgent($agent);
return AdminPermissionBridge::legacySlugsGrantedByMenuActionCodes($codes);
}
/**
* @return list<string> prd.*
*/
public static function delegationLegacySlugsForAdminUser(AdminUser $admin): array
{
if ($admin->isSuperAdmin()) {
return AdminPermissionBridge::allLegacySlugs();
}
$node = $admin->primaryAgentNode();
if ($node === null) {
return [];
}
return self::delegationLegacySlugsForAgent($node);
}
public static function childIsManageableBy(AdminUser $admin, AgentNode $child): bool
{
if ($admin->isSuperAdmin()) {
return true;
}
if (! AdminAgentScope::nodeVisibleTo($admin, $child)) {
return false;
}
$parentId = $child->parent_id;
if ($parentId === null) {
return false;
}
$actor = AdminAgentScope::primaryAgentNode($admin);
if ($actor === null) {
return false;
}
return (int) $parentId === (int) $actor->id
|| AdminAgentScope::nodeManageableBy($admin, AgentNode::query()->find($parentId) ?? $child);
}
/**
* @param list<array{menu_action_id: int, can_delegate?: bool}> $grants
*/
public static function assertGrantsAllowed(AdminUser $actor, AgentNode $child, array $grants): void
{
if ($actor->isSuperAdmin()) {
return;
}
if (! self::childIsManageableBy($actor, $child)) {
throw ValidationException::withMessages(['child_agent_id' => ['not_manageable']]);
}
$actorCodes = $actor->effectiveMenuActionPermissionCodes();
$actorCodeSet = array_fill_keys($actorCodes, true);
$parent = $child->parent;
if ($parent === null && $child->parent_id !== null) {
$parent = AgentNode::query()->find($child->parent_id);
}
$parentCeiling = $parent !== null
? self::delegationMenuActionCodesForAgent($parent)
: $actorCodes;
if ($parent !== null && ! $parent->isRoot() && $parentCeiling === []) {
$parentCeiling = $actorCodes;
}
$parentCeilingSet = array_fill_keys($parentCeiling, true);
foreach ($grants as $grant) {
$actionId = (int) ($grant['menu_action_id'] ?? 0);
$code = DB::table('admin_menu_actions')->where('id', $actionId)->value('permission_code');
if (! is_string($code) || $code === '') {
throw ValidationException::withMessages(['grants' => ['invalid_menu_action']]);
}
if (! isset($actorCodeSet[$code])) {
throw ValidationException::withMessages(['grants' => ['exceeds_actor: '.$code]]);
}
if ($parent !== null && ! $parent->isRoot() && ! isset($parentCeilingSet[$code])) {
throw ValidationException::withMessages(['grants' => ['exceeds_parent_ceiling: '.$code]]);
}
}
}
/**
* @param list<string> $permissionSlugs
*/
public static function assertRoleSlugsWithinAgentCeiling(
AgentNode $ownerAgent,
array $permissionSlugs,
AdminUser $actor,
): void {
$ceiling = self::delegationLegacySlugsForAgent($ownerAgent);
if ($ceiling === []) {
// 尚未配置下放上限:与 P2 一致,仅校验操作者自身权限
AgentRoleAuthorization::assertSlugsWithinActor($actor, $permissionSlugs);
return;
}
$invalid = array_values(array_diff($permissionSlugs, $ceiling));
if ($invalid !== []) {
throw ValidationException::withMessages([
'permission_slugs' => ['exceeds_delegation_ceiling: '.implode(', ', $invalid)],
]);
}
}
}