fix(gerp): role_scope负责人/组长判定改读关联表RoleType,支持多部门作用域与公司过滤
- 移除UserRole遗留字段(RoleType/DeptUUID/WorkgroupUUID)相关隐藏与防写入规则 - 同步UserRole/UserRoleDepartment列展示优化
This commit is contained in:
1 parent
741bfb7740
commit
058fd11fb0
3 files changed
+74
-67
No files matched your search
@@ -154,7 +154,7 @@ class FormService(FormServiceBase):
|
||||
}
|
||||
|
||||
# 列表页表格隐藏主键 UUID 列的表(UUID 无业务展示意义,仅前端隐藏,后端仍返回)
|
||||
HIDE_PK_UUID_IN_TABLE = {'Company', 'Department', 'Workgroup'}
|
||||
HIDE_PK_UUID_IN_TABLE = {'Company', 'Department', 'Workgroup', 'UserRoleDepartment', 'UserRole'}
|
||||
|
||||
def __init__(self, user, current_company_uuid=None, admin_verify=False, current_role_uuid=None, current_tenant_uuid=None, ip_address='', role_scope=None):
|
||||
super().__init__(user=user)
|
||||
@@ -726,9 +726,18 @@ class FormService(FormServiceBase):
|
||||
col['type'] = 'readonly'
|
||||
col['editable'] = False
|
||||
col['hideInForm'] = True
|
||||
# UserRole 遗留字段(DeptUUID/WorkgroupUUID):部门/工作组归属已迁移到
|
||||
# UserRoleDepartment/UserRoleWorkgroup 关联表,表单+列表均隐藏,禁止再次写入
|
||||
if table_name == 'UserRole' and pascal_name in ('DeptUUID', 'WorkgroupUUID'):
|
||||
# UserRole 列表可读性:UserUUID/RoleUUID 值已由 _enrich_entity_labels 回显为
|
||||
# 用户名/角色名,列标题直接改"用户名"/"角色名"(避免误导为裸 UUID);
|
||||
# CompanyUUID 为隔离冗余列,角色成员列表均按公司隔离展示,列表隐藏
|
||||
if table_name == 'UserRole' and pascal_name == 'UserUUID':
|
||||
col['title'] = '用户名'
|
||||
if table_name == 'UserRole' and pascal_name == 'RoleUUID':
|
||||
col['title'] = '角色名'
|
||||
if table_name == 'UserRole' and pascal_name == 'CompanyUUID':
|
||||
col['visible'] = False
|
||||
# UserRoleDepartment 关联表冗余字段(DeptUUID/TenantUUID/CompanyUUID):
|
||||
# 仅用于租户/公司隔离过滤与详情页上下文注入,列表+表单均隐藏(详情页由 query 注入)
|
||||
if table_name == 'UserRoleDepartment' and pascal_name in ('DeptUUID', 'TenantUUID', 'CompanyUUID'):
|
||||
col['editable'] = False
|
||||
col['hideInForm'] = True
|
||||
col['visible'] = False
|
||||
@@ -868,7 +877,7 @@ class FormService(FormServiceBase):
|
||||
# 系统级表(Menu/Permission)豁免隔离
|
||||
field_names = {f.name for f in model._meta.get_fields() if hasattr(f, 'name')}
|
||||
# 租户管理(Tenant 表)仅租户管理员/跨租户管理员可读;普通用户无权限(菜单已隐藏,此处数据兜底)
|
||||
if table_name == 'Tenant' and not self._is_admin():
|
||||
if table_name == 'Tenant' and not self._is_admin(self.user):
|
||||
return {'records': [], 'total': 0, 'page': 1, 'PageSize': 20}
|
||||
if table_name not in self.COMPANY_SCOPE_EXEMPT_TABLES:
|
||||
# 公司隔离:有 CompanyUUID 字段的表按当前公司过滤
|
||||
@@ -1464,9 +1473,6 @@ class FormService(FormServiceBase):
|
||||
_valid_values = [opt['value'] for opt in _opt_map[resolved]]
|
||||
if val not in _valid_values:
|
||||
return {'error': f"字段 '{resolved}' 值非法:只允许 {_valid_values}", 'field': resolved}
|
||||
# 遗留字段防写入:UserRole 的 DeptUUID/WorkgroupUUID 已迁移到关联表,禁止再改
|
||||
if table_name == 'UserRole' and resolved in ('DeptUUID', 'WorkgroupUUID'):
|
||||
continue
|
||||
# 订单受保护字段:状态机/审计字段禁止通用修改(状态流转必须走专用端点)
|
||||
if table_name == 'Order' and resolved in self.ORDER_PROTECTED_FIELDS:
|
||||
continue
|
||||
|
||||
@@ -3,14 +3,17 @@
|
||||
|
||||
作用域类型:
|
||||
admin —— 管理员(当前角色拥有 admin:super 或 admin:tenant 权限),看全公司
|
||||
dept_manager —— 部门负责人(UserRoleDepartment 关联 + RoleType=1),看本部门成员
|
||||
workgroup_lead —— 工作组长(UserRoleWorkgroup 关联 + RoleType=1),看本工作组成员
|
||||
dept_manager —— 部门负责人(UserRoleDepartment 关联行 RoleType=1),看本部门成员
|
||||
workgroup_lead —— 工作组长(UserRoleWorkgroup 关联行 RoleType=1),看本工作组成员
|
||||
member —— 普通组员,只看自己
|
||||
|
||||
负责人/组长判定一律以关联表 RoleType=1 为准(UserRole.RoleType / DeptUUID /
|
||||
WorkgroupUUID 遗留字段已废弃删除,见 gvsdsdk 0002 迁移)。
|
||||
|
||||
用法:
|
||||
scope = GetUserScope(request)
|
||||
if scope.Type == 'dept_manager':
|
||||
qs = qs.filter(DeptUUID=scope.DeptUUID)
|
||||
qs = qs.filter(DeptUUID__in=scope.DeptUUIDs)
|
||||
"""
|
||||
import logging
|
||||
import uuid as _uuid
|
||||
@@ -21,10 +24,10 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class RoleScope(NamedTuple):
|
||||
Type: str # admin / dept_manager / workgroup_lead / member
|
||||
DeptUUID: Optional[bytes] # 部门 UUID(dept_manager 时非空)
|
||||
WorkgroupUUID: Optional[bytes] # 工作组 UUID(workgroup_lead 时非空)
|
||||
DeptUUIDs: Optional[tuple] # 部门 UUID 列表(dept_manager 时非空,可多部门)
|
||||
WorkgroupUUIDs: Optional[tuple] # 工作组 UUID 列表(workgroup_lead 时非空)
|
||||
UserUUID: Optional[bytes] # 当前用户 UUID(member 时用于过滤)
|
||||
RoleType: int # 当前角色类型
|
||||
RoleType: int # 0=成员 1=负责人/组长(以关联表 RoleType 为准)
|
||||
|
||||
|
||||
def _to_bytes(value):
|
||||
@@ -56,9 +59,12 @@ def GetUserScope(request) -> RoleScope:
|
||||
|
||||
优先级:
|
||||
1. 管理员(IsAdmin=true)→ admin
|
||||
2. 当前 UserRole 的 RoleType=1 且 DeptUUID 非空 → dept_manager
|
||||
3. 当前 UserRole 的 RoleType=1 且 WorkgroupUUID 非空 → workgroup_lead
|
||||
2. 当前 (用户, 角色) 关联在 UserRoleDepartment 有 RoleType=1 的行 → dept_manager
|
||||
3. 当前 (用户, 角色) 关联在 UserRoleWorkgroup 有 RoleType=1 的行 → workgroup_lead
|
||||
4. 其他 → member
|
||||
|
||||
负责人/组长判定以关联表 RoleType 为准(UserRole.RoleType / DeptUUID /
|
||||
WorkgroupUUID 遗留字段已废弃删除),并按当前公司过滤(跨公司同名对不串)。
|
||||
"""
|
||||
user = getattr(request, 'user', None) if request else None
|
||||
user_uuid = getattr(user, 'UserUUID', None) if user else None
|
||||
@@ -68,13 +74,13 @@ def GetUserScope(request) -> RoleScope:
|
||||
if IsAdmin(request):
|
||||
return RoleScope(
|
||||
Type='admin',
|
||||
DeptUUID=None,
|
||||
WorkgroupUUID=None,
|
||||
DeptUUIDs=None,
|
||||
WorkgroupUUIDs=None,
|
||||
UserUUID=user_uuid_bytes,
|
||||
RoleType=1,
|
||||
)
|
||||
|
||||
# 获取当前角色关联的 UserRole 记录(含 DeptUUID/WorkgroupUUID/RoleType)
|
||||
# 获取当前角色关联的 UserRole 记录(RoleType 等遗留字段已废弃,仅取关联 UUID)
|
||||
current_role = getattr(request, 'current_role', None)
|
||||
role_uuid = getattr(current_role, 'RoleUUID', None) if current_role else None
|
||||
current_company = getattr(request, 'current_company_uuid', None)
|
||||
@@ -90,8 +96,8 @@ def GetUserScope(request) -> RoleScope:
|
||||
if not role_uuid or not user_uuid_bytes:
|
||||
return RoleScope(
|
||||
Type='member',
|
||||
DeptUUID=None,
|
||||
WorkgroupUUID=None,
|
||||
DeptUUIDs=None,
|
||||
WorkgroupUUIDs=None,
|
||||
UserUUID=user_uuid_bytes,
|
||||
RoleType=0,
|
||||
)
|
||||
@@ -105,60 +111,46 @@ def GetUserScope(request) -> RoleScope:
|
||||
if not ur:
|
||||
return RoleScope(
|
||||
Type='member',
|
||||
DeptUUID=None,
|
||||
WorkgroupUUID=None,
|
||||
DeptUUIDs=None,
|
||||
WorkgroupUUIDs=None,
|
||||
UserUUID=user_uuid_bytes,
|
||||
RoleType=0,
|
||||
)
|
||||
|
||||
role_type = ur.RoleType or 0
|
||||
ur_uuid_bytes = _to_bytes(ur.UserRoleUUID)
|
||||
# 部门/工作组归属以关联表为准(UserRole.DeptUUID/WorkgroupUUID 已废弃清空):
|
||||
# 部门负责人/工作组长通过 UserRoleDepartment / UserRoleWorkgroup 关联行判定
|
||||
dept_uuid = None
|
||||
workgroup_uuid = None
|
||||
if role_type == 1 and ur_uuid_bytes:
|
||||
try:
|
||||
urd = RoleManager.GetUserRoleDepartment(ur_uuid_bytes)
|
||||
if urd:
|
||||
dept_uuid = _to_bytes(urd.DeptUUID)
|
||||
except Exception:
|
||||
dept_uuid = None
|
||||
if not dept_uuid:
|
||||
try:
|
||||
urw = RoleManager.GetUserRoleWorkgroup(ur_uuid_bytes)
|
||||
if urw:
|
||||
workgroup_uuid = _to_bytes(urw.WorkgroupUUID)
|
||||
except Exception:
|
||||
workgroup_uuid = None
|
||||
|
||||
# 2. 部门负责人
|
||||
if role_type == 1 and dept_uuid:
|
||||
# 2. 部门负责人:关联表 UserRoleDepartment 中 RoleType=1 的行(按当前公司过滤)
|
||||
manager_depts = RoleManager.GetUserRoleDepartments(
|
||||
ur_uuid_bytes, CompanyUUID=company_uuid_bytes, RoleType=1,
|
||||
)
|
||||
if manager_depts:
|
||||
return RoleScope(
|
||||
Type='dept_manager',
|
||||
DeptUUID=dept_uuid,
|
||||
WorkgroupUUID=None,
|
||||
DeptUUIDs=tuple(d for d in (_to_bytes(x.DeptUUID) for x in manager_depts) if d),
|
||||
WorkgroupUUIDs=None,
|
||||
UserUUID=user_uuid_bytes,
|
||||
RoleType=role_type,
|
||||
RoleType=1,
|
||||
)
|
||||
|
||||
# 3. 工作组长
|
||||
if role_type == 1 and workgroup_uuid:
|
||||
# 3. 工作组长:关联表 UserRoleWorkgroup 中 RoleType=1 的行(按当前公司过滤)
|
||||
manager_wgs = RoleManager.GetUserRoleWorkgroups(
|
||||
ur_uuid_bytes, CompanyUUID=company_uuid_bytes, RoleType=1,
|
||||
)
|
||||
if manager_wgs:
|
||||
return RoleScope(
|
||||
Type='workgroup_lead',
|
||||
DeptUUID=None,
|
||||
WorkgroupUUID=workgroup_uuid,
|
||||
DeptUUIDs=None,
|
||||
WorkgroupUUIDs=tuple(w for w in (_to_bytes(x.WorkgroupUUID) for x in manager_wgs) if w),
|
||||
UserUUID=user_uuid_bytes,
|
||||
RoleType=role_type,
|
||||
RoleType=1,
|
||||
)
|
||||
|
||||
# 4. 普通组员
|
||||
return RoleScope(
|
||||
Type='member',
|
||||
DeptUUID=None,
|
||||
WorkgroupUUID=None,
|
||||
DeptUUIDs=None,
|
||||
WorkgroupUUIDs=None,
|
||||
UserUUID=user_uuid_bytes,
|
||||
RoleType=role_type,
|
||||
RoleType=0,
|
||||
)
|
||||
|
||||
|
||||
@@ -245,12 +237,19 @@ def ApplyScopeToQueryset(qs, scope: RoleScope, table_name: str):
|
||||
def _get_scope_members(scope: RoleScope):
|
||||
"""获取当前作用域下的成员 UUID 列表(委托 gvsdsdk RoleManager.GetScopeMembers)
|
||||
|
||||
dept_manager: 查询 UserRoleDepartment 中 DeptUUID = scope.DeptUUID 的用户
|
||||
workgroup_lead: 查询 UserRoleWorkgroup 中 WorkgroupUUID = scope.WorkgroupUUID 的用户
|
||||
dept_manager: 查询 UserRoleDepartment 中 DeptUUID ∈ scope.DeptUUIDs 的用户(多部门并集)
|
||||
workgroup_lead: 查询 UserRoleWorkgroup 中 WorkgroupUUID ∈ scope.WorkgroupUUIDs 的用户
|
||||
"""
|
||||
from gvsdsdk.auth.managers import RoleManager
|
||||
if scope.Type == 'dept_manager' and scope.DeptUUID:
|
||||
return RoleManager.GetScopeMembers('dept_manager', scope.DeptUUID)
|
||||
if scope.Type == 'workgroup_lead' and scope.WorkgroupUUID:
|
||||
return RoleManager.GetScopeMembers('workgroup_lead', scope.WorkgroupUUID)
|
||||
return None
|
||||
member_uuids = set()
|
||||
if scope.Type == 'dept_manager' and scope.DeptUUIDs:
|
||||
for dept_uuid in scope.DeptUUIDs:
|
||||
members = RoleManager.GetScopeMembers('dept_manager', dept_uuid)
|
||||
if members:
|
||||
member_uuids.update(members)
|
||||
elif scope.Type == 'workgroup_lead' and scope.WorkgroupUUIDs:
|
||||
for wg_uuid in scope.WorkgroupUUIDs:
|
||||
members = RoleManager.GetScopeMembers('workgroup_lead', wg_uuid)
|
||||
if members:
|
||||
member_uuids.update(members)
|
||||
return list(member_uuids) if scope.Type in ('dept_manager', 'workgroup_lead') else None
|
||||
@@ -418,10 +418,12 @@ class CheckAdminView(ELTAuthenticatedAPIView):
|
||||
scope = GetUserScope(request)
|
||||
scope_type = scope.Type
|
||||
scope_label = self.SCOPE_TYPE_LABEL.get(scope_type, '普通组员')
|
||||
if scope.DeptUUID:
|
||||
dept_uuid_str = UUIDObj(scope.DeptUUID).str
|
||||
if scope.WorkgroupUUID:
|
||||
workgroup_uuid_str = UUIDObj(scope.WorkgroupUUID).str
|
||||
# 负责人/组长可挂多个部门/工作组:多个 UUID 用逗号拼接
|
||||
# (前端当前仅消费 Type/IsDeptManager/IsWorkgroupLead,列表值供扩展)
|
||||
if scope.DeptUUIDs:
|
||||
dept_uuid_str = ','.join(UUIDObj(d).str for d in scope.DeptUUIDs if d)
|
||||
if scope.WorkgroupUUIDs:
|
||||
workgroup_uuid_str = ','.join(UUIDObj(w).str for w in scope.WorkgroupUUIDs if w)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
Reference in new issue
Block a user