"chore(gvsdsdk): 清理过时点与死代码并修复运行期缺陷
- models: 修正过时文档、UserRole 遗留字段标注废弃、抽象 User 补 IsStaff/IsSuperuser - models: CopyToRole 补 CompanyUUID 默认值,修复 RolePermission 插入必失败缺陷 - form_service: 对齐主项目值转换(Boolean/时区/空值),移除 NullBooleanField 遗留映射 - auth: 删除无引用的 GetDefaultAvatar 与 ShopManager/PositionManager,清理惰性导出 - db_router/model_base/msync: 简化模块集合、移除 typing_extensions 死兜底、复用 GenerateChallenge - 删除残留文件(空测试文件、无源 pyc)"
This commit is contained in:
1 parent
32a31a8220
commit
e9bde4db76
19 files changed
+66
-220
No files matched your search
+1
-2
@@ -13,11 +13,10 @@ GVSDS 分布式服务间通信 SDK,作为子服务器(如 ATestCServer)和
|
||||
包结构:
|
||||
gvsdsdk/
|
||||
├── auth/ # 用户认证与权限模块
|
||||
│ ├── cookie.py # 默认头像等工具
|
||||
│ ├── crypto.py # 密码哈希 + AES 加密
|
||||
│ ├── middleware.py # CurrentRoleMiddleware
|
||||
│ ├── permissions.py # RBAC/ABAC 权限
|
||||
│ └── managers.py # UserManager + RoleManager
|
||||
│ └── managers.py # RoleManager(UserManager 在 models.py)
|
||||
├── msync/ # MSYNC 服务间通信
|
||||
├── commission/ # 分佣引擎
|
||||
│ ├── models.py # 分佣数据模型
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
-14
@@ -1,21 +1,16 @@
|
||||
"""gvsdsdk.auth — 用户认证与权限模块"""
|
||||
|
||||
__all__ = [
|
||||
'GetDefaultAvatar',
|
||||
'HashPassword', 'VerifyPassword', 'SymmetricEncrypt', 'SymmetricDecrypt', 'FileSHA1', 'FileSHA256', 'BytesSHA1', 'BytesSHA256',
|
||||
'CurrentRoleMiddleware',
|
||||
'IsAdminUser', 'IsCompanyMember', 'CompanyScopedMixin', 'TenantScopedMixin', 'UserScopedMixin', 'PermissionVerifier',
|
||||
'IsAdminScopeDisabled', 'IsTenantScopeDisabled',
|
||||
'IsAdmin', 'IsSuperAdmin', 'IsTenantAdmin',
|
||||
'RoleManager', 'ShopManager', 'PositionManager',
|
||||
'RoleManager',
|
||||
]
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
if name == 'GetDefaultAvatar':
|
||||
from gvsdsdk.auth.cookie import GetDefaultAvatar
|
||||
globals()['GetDefaultAvatar'] = GetDefaultAvatar
|
||||
return GetDefaultAvatar
|
||||
if name in ('HashPassword', 'VerifyPassword', 'SymmetricEncrypt', 'SymmetricDecrypt', 'FileSHA1', 'FileSHA256', 'BytesSHA1', 'BytesSHA256'):
|
||||
from gvsdsdk.auth import crypto as _m
|
||||
val = getattr(_m, name)
|
||||
@@ -39,12 +34,4 @@ def __getattr__(name):
|
||||
from gvsdsdk.auth.managers import RoleManager
|
||||
globals()['RoleManager'] = RoleManager
|
||||
return RoleManager
|
||||
if name == 'ShopManager':
|
||||
from gvsdsdk.auth.managers import ShopManager
|
||||
globals()['ShopManager'] = ShopManager
|
||||
return ShopManager
|
||||
if name == 'PositionManager':
|
||||
from gvsdsdk.auth.managers import PositionManager
|
||||
globals()['PositionManager'] = PositionManager
|
||||
return PositionManager
|
||||
raise AttributeError(f"module 'gvsdsdk.auth' has no attribute {name!r}")
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,11 +0,0 @@
|
||||
from django.http import HttpResponse
|
||||
|
||||
|
||||
def GetDefaultAvatar():
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
img = Image.new('RGB', (256, 256), (200, 200, 200))
|
||||
buf = BytesIO()
|
||||
img.save(buf, format='PNG')
|
||||
buf.seek(0)
|
||||
return HttpResponse(buf.read(), content_type='image/png')
|
||||
+2
-151
@@ -1,11 +1,10 @@
|
||||
import uuid
|
||||
from django.utils import timezone as _timezone
|
||||
|
||||
from gvsdsdk.models import Role, UserRole, RolePermission, Permission, Shop, RoleShop, Position, PositionTemplate, PositionTemplatePermission
|
||||
from gvsdsdk.models import Role, UserRole, RolePermission, Permission
|
||||
from gvsdsdk.model_utils import _GetUserModel
|
||||
|
||||
# NOTE: 类型注解(L8)暂缓——本模块含 RoleManager/ShopManager/PositionManager 共 20+ 静态方法,
|
||||
# 全量补充参数与返回值注解工作量较大且易引入回归,后续单独迭代处理。
|
||||
# NOTE: 类型注解暂缓——RoleManager 静态方法较多,全量补充注解工作量较大且易引入回归,后续单独迭代处理。
|
||||
|
||||
|
||||
class RoleManager:
|
||||
@@ -139,151 +138,3 @@ class RoleManager:
|
||||
perm_uuids = qs.values_list('PermUUID', flat=True)
|
||||
return Permission.objects.filter(PermUUID__in=perm_uuids, PermStatus=1)
|
||||
|
||||
|
||||
class ShopManager:
|
||||
"""商铺管理器——提供商铺创建、角色关联、查询"""
|
||||
|
||||
@staticmethod
|
||||
def CreateShop(Name, CompanyUUID, Code='', ShopType=0, **Kwargs):
|
||||
"""创建商铺"""
|
||||
shop = Shop(
|
||||
ShopUUID=uuid.uuid4().bytes,
|
||||
CompanyUUID=CompanyUUID,
|
||||
ShopName=Name,
|
||||
ShopCode=Code,
|
||||
ShopType=ShopType,
|
||||
ShopStatus=1,
|
||||
ShopAddress=Kwargs.get('ShopAddress', ''),
|
||||
ShopContact=Kwargs.get('ShopContact', ''),
|
||||
ShopPhone=Kwargs.get('ShopPhone', ''),
|
||||
ShopDesc=Kwargs.get('ShopDesc', ''),
|
||||
CreateTime=_timezone.now(),
|
||||
UpdateTime=_timezone.now(),
|
||||
)
|
||||
shop.save()
|
||||
return shop
|
||||
|
||||
@staticmethod
|
||||
def AssignRole(RoleUUID, ShopUUID, IsMainShop=False):
|
||||
"""为角色关联商铺"""
|
||||
role_shop = RoleShop(
|
||||
RoleShopUUID=uuid.uuid4().bytes,
|
||||
RoleUUID=RoleUUID,
|
||||
ShopUUID=ShopUUID,
|
||||
IsMainShop=1 if IsMainShop else 0,
|
||||
CreateTime=_timezone.now(),
|
||||
)
|
||||
role_shop.save()
|
||||
return role_shop
|
||||
|
||||
@staticmethod
|
||||
def RemoveRole(RoleUUID, ShopUUID):
|
||||
"""移除角色与商铺的关联"""
|
||||
deleted, _ = RoleShop.objects.filter(RoleUUID=RoleUUID, ShopUUID=ShopUUID).delete()
|
||||
return deleted > 0
|
||||
|
||||
@staticmethod
|
||||
def GetShopsForRole(RoleUUID):
|
||||
"""获取角色关联的所有商铺"""
|
||||
shop_uuids = RoleShop.objects.filter(RoleUUID=RoleUUID).values_list('ShopUUID', flat=True)
|
||||
return Shop.objects.filter(ShopUUID__in=shop_uuids)
|
||||
|
||||
@staticmethod
|
||||
def GetRolesForShop(ShopUUID):
|
||||
"""获取商铺关联的所有角色"""
|
||||
role_uuids = RoleShop.objects.filter(ShopUUID=ShopUUID).values_list('RoleUUID', flat=True)
|
||||
return Role.objects.filter(RoleUUID__in=role_uuids)
|
||||
|
||||
@staticmethod
|
||||
def GetMainShopForRole(RoleUUID):
|
||||
"""获取角色的主商铺"""
|
||||
rs = RoleShop.objects.filter(RoleUUID=RoleUUID, IsMainShop=1).first()
|
||||
if rs:
|
||||
return Shop.objects.filter(ShopUUID=rs.ShopUUID).first()
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def GetShopsForCompany(CompanyUUID):
|
||||
"""获取公司下的所有商铺"""
|
||||
return Shop.objects.filter(CompanyUUID=CompanyUUID, ShopStatus=1)
|
||||
|
||||
|
||||
class PositionManager:
|
||||
"""职务管理器——提供人事职务标签创建、岗位模板管理"""
|
||||
|
||||
@staticmethod
|
||||
def CreatePosition(Name, TenantUUID, Code='', Level=0, **Kwargs):
|
||||
"""创建人事职务标签"""
|
||||
position = Position(
|
||||
PositionUUID=uuid.uuid4().bytes,
|
||||
TenantUUID=TenantUUID,
|
||||
PositionName=Name,
|
||||
PositionCode=Code,
|
||||
PositionLevel=Level,
|
||||
PositionStatus=1,
|
||||
PositionDesc=Kwargs.get('PositionDesc', ''),
|
||||
CreateTime=_timezone.now(),
|
||||
UpdateTime=_timezone.now(),
|
||||
)
|
||||
position.save()
|
||||
return position
|
||||
|
||||
@staticmethod
|
||||
def GetPositionsForTenant(TenantUUID):
|
||||
"""获取租户下的所有职务"""
|
||||
return Position.objects.filter(TenantUUID=TenantUUID, PositionStatus=1)
|
||||
|
||||
@staticmethod
|
||||
def CreatePositionTemplate(Name, TenantUUID, Code='', **Kwargs):
|
||||
"""创建岗位模板"""
|
||||
template = PositionTemplate(
|
||||
PositionTemplateUUID=uuid.uuid4().bytes,
|
||||
TenantUUID=TenantUUID,
|
||||
TemplateName=Name,
|
||||
TemplateCode=Code,
|
||||
TemplateStatus=1,
|
||||
TemplateDesc=Kwargs.get('TemplateDesc', ''),
|
||||
CreateTime=_timezone.now(),
|
||||
UpdateTime=_timezone.now(),
|
||||
)
|
||||
template.save()
|
||||
return template
|
||||
|
||||
@staticmethod
|
||||
def AssignPermToTemplate(TemplateUUID, PermUUID):
|
||||
"""为岗位模板关联权限"""
|
||||
ptp = PositionTemplatePermission(
|
||||
PTPermUUID=uuid.uuid4().bytes,
|
||||
PositionTemplateUUID=TemplateUUID,
|
||||
PermUUID=PermUUID,
|
||||
CreateTime=_timezone.now(),
|
||||
)
|
||||
ptp.save()
|
||||
return ptp
|
||||
|
||||
@staticmethod
|
||||
def RemovePermFromTemplate(TemplateUUID, PermUUID):
|
||||
"""移除岗位模板与权限的关联"""
|
||||
deleted, _ = PositionTemplatePermission.objects.filter(
|
||||
PositionTemplateUUID=TemplateUUID, PermUUID=PermUUID
|
||||
).delete()
|
||||
return deleted > 0
|
||||
|
||||
@staticmethod
|
||||
def GetPermsForTemplate(TemplateUUID):
|
||||
"""获取岗位模板关联的所有权限"""
|
||||
perm_uuids = PositionTemplatePermission.objects.filter(
|
||||
PositionTemplateUUID=TemplateUUID
|
||||
).values_list('PermUUID', flat=True)
|
||||
return Permission.objects.filter(PermUUID__in=perm_uuids, PermStatus=1)
|
||||
|
||||
@staticmethod
|
||||
def GetTemplatesForTenant(TenantUUID):
|
||||
"""获取租户下的所有岗位模板"""
|
||||
return PositionTemplate.objects.filter(TenantUUID=TenantUUID, TemplateStatus=1)
|
||||
|
||||
@staticmethod
|
||||
def CopyTemplateToRole(TemplateUUID, RoleName, TenantUUID):
|
||||
"""从岗位模板复制权限到新角色"""
|
||||
template = PositionTemplate.objects.get(PositionTemplateUUID=TemplateUUID)
|
||||
return template.CopyToRole(RoleName, TenantUUID)
|
||||
+4
-10
@@ -35,14 +35,8 @@ class SAASDatabaseRouter:
|
||||
GVSDSDK_SAAS_READ_ONLY = True # 默认 True(子服务器只读)
|
||||
"""
|
||||
|
||||
# gvsdsdk 内所有模块前缀
|
||||
_GVSDSDK_MODULES = frozenset({
|
||||
'gvsdsdk.models',
|
||||
'gvsdsdk.commission.models',
|
||||
'gvsdsdk.payment.models',
|
||||
})
|
||||
|
||||
# gvsdsdk 内所有 app_label
|
||||
# gvsdsdk 内所有模块前缀(startswith 判定覆盖全部子模块,
|
||||
# 无需维护显式模块名清单,避免新增模块时遗漏)
|
||||
_GVSDSDK_APP_LABELS = frozenset({
|
||||
'gvsdsdk',
|
||||
})
|
||||
@@ -57,9 +51,9 @@ class SAASDatabaseRouter:
|
||||
|
||||
def _is_gvsdsdk_model(self, model):
|
||||
"""判断模型是否属于 gvsdsdk"""
|
||||
# 按模块路径判断
|
||||
# 按模块路径判断(gvsdsdk.* 前缀全覆盖)
|
||||
module = getattr(model, '__module__', '')
|
||||
if module in self._GVSDSDK_MODULES or module.startswith('gvsdsdk.'):
|
||||
if module.startswith('gvsdsdk.'):
|
||||
return True
|
||||
# 按 app_label 判断
|
||||
app_label = getattr(model._meta, 'app_label', '')
|
||||
|
||||
+30
-8
@@ -14,9 +14,8 @@
|
||||
'MyModel2': MyModel2,
|
||||
}
|
||||
|
||||
# 子服务器 views.py
|
||||
from gvsdsdk.form_service import FormServiceAPIView
|
||||
from .form_service import FormService
|
||||
# 子服务器 views.py 中直接实例化 FormService 使用
|
||||
# (FormServiceBase 提供 ORM 能力,视图层由子服务器自行封装)
|
||||
"""
|
||||
|
||||
import uuid
|
||||
@@ -87,6 +86,17 @@ class FormServiceBase:
|
||||
if val is None or val == '':
|
||||
if getattr(field, 'null', False):
|
||||
return None
|
||||
# 非空字段:数值类型返回默认值或0,布尔类型返回 False,其他类型返回空字符串
|
||||
field_type = field.__class__.__name__
|
||||
if field_type in ('SmallIntegerField', 'IntegerField', 'BigIntegerField',
|
||||
'PositiveIntegerField', 'PositiveSmallIntegerField',
|
||||
'FloatField', 'DecimalField'):
|
||||
default = getattr(field, 'default', None)
|
||||
if default is not None:
|
||||
return default
|
||||
return 0
|
||||
if field_type == 'BooleanField':
|
||||
return False
|
||||
return val
|
||||
field_type = field.__class__.__name__
|
||||
if field_type == 'UUIDField' and isinstance(val, str):
|
||||
@@ -115,12 +125,25 @@ class FormServiceBase:
|
||||
return val
|
||||
if field_type == 'DateTimeField' and isinstance(val, str):
|
||||
try:
|
||||
return datetime.datetime.fromisoformat(val)
|
||||
dt = datetime.datetime.fromisoformat(val)
|
||||
# USE_TZ=False + MySQL 不接受 aware datetime:
|
||||
# 统一转 naive UTC(UTC-0 存储,展示时由前端按用户时区转换)
|
||||
if dt.tzinfo is not None:
|
||||
dt = dt.astimezone(datetime.timezone.utc).replace(tzinfo=None)
|
||||
return dt
|
||||
except (ValueError, TypeError):
|
||||
return val
|
||||
if hasattr(field, 'max_length') and field.max_length is not None and isinstance(val, str) and len(val) > field.max_length:
|
||||
logger.warning('字段 %s 值超长(%d > %d),已截断', getattr(field, 'name', '?'), len(val), field.max_length)
|
||||
val = val[:field.max_length]
|
||||
if field_type in ('BooleanField', 'NullBooleanField') and isinstance(val, str):
|
||||
low = val.strip().lower()
|
||||
if low in ('true', '1', 't', 'yes'):
|
||||
return True
|
||||
if low in ('false', '0', 'f', 'no'):
|
||||
return False
|
||||
return val
|
||||
max_length = getattr(field, 'max_length', None)
|
||||
if max_length and isinstance(val, str) and len(val) > max_length:
|
||||
logger.warning('字段 %s 值超长(%d > %d),已截断', getattr(field, 'name', '?'), len(val), max_length)
|
||||
val = val[:max_length]
|
||||
return val
|
||||
|
||||
@staticmethod
|
||||
@@ -141,7 +164,6 @@ class FormServiceBase:
|
||||
'FloatField': 'number',
|
||||
'DecimalField': 'number',
|
||||
'BooleanField': 'boolean',
|
||||
'NullBooleanField': 'boolean',
|
||||
'DateTimeField': 'datetime',
|
||||
'DateField': 'date',
|
||||
'TimeField': 'time',
|
||||
|
||||
+1
-6
@@ -24,12 +24,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Any, Callable, Dict, Generic, Optional, Tuple, Type, TypeVar, Union
|
||||
|
||||
try:
|
||||
from typing import Self
|
||||
except ImportError:
|
||||
from typing_extensions import Self
|
||||
from typing import Any, Callable, Dict, Generic, Optional, Self, Tuple, Type, TypeVar, Union
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
供子服务器直接进行跨服务器 ORM 查询。
|
||||
|
||||
权限体系四大分层:
|
||||
1. 组织人事层:Tenant → Company → Department,员工通过 UserRole.DeptUUID 归属部门
|
||||
1. 组织人事层:Tenant → Company → Department,员工通过 UserRoleDepartment/UserRoleWorkgroup 关联表归属部门/工作组
|
||||
2. 岗位模板层:PositionTemplate + PositionTemplatePermission,权限初始化快照
|
||||
3. 角色权限载体层:Role + RolePermission,运行时鉴权实体
|
||||
4. 鉴权规则层:操作权限校验(角色汇总)+ 数据权限校验(组织属性)
|
||||
@@ -15,11 +15,13 @@
|
||||
使用方式(子服务器):
|
||||
from gvsdsdk.models import Permission, Role, UserRole, RolePermission
|
||||
|
||||
# 查询所有启用的用户(需在业务子类中定义具体 User 模型)
|
||||
users = User.objects.filter(UserAccountLicense=1)
|
||||
# 查询所有启用的用户(User 为抽象基类,需经 _GetUserModel 获取具体模型)
|
||||
from gvsdsdk.model_utils import _GetUserModel
|
||||
UserModel = _GetUserModel()
|
||||
users = UserModel.objects.filter(UserAccountLicense=1)
|
||||
|
||||
# 或配置 DatabaseRouter 后直接使用
|
||||
users = User.objects.filter(UserAccountLicense=1)
|
||||
users = UserModel.objects.filter(UserAccountLicense=1)
|
||||
|
||||
设计原则:
|
||||
1. 独立:不依赖 gvsds 内部模块,可在任意 Django 项目中独立使用
|
||||
@@ -29,8 +31,7 @@
|
||||
5. 租户隔离:所有权限相关数据依附于独立租户空间,不同租户互相不可见
|
||||
|
||||
注意:User 为抽象基类,仅包含 SAAS 主库标准字段。
|
||||
业务子类应继承 User 并在单独文件中添加业务字段和方法。
|
||||
参见 gvsdsdk/business_models.py。
|
||||
业务子类应继承 User 并添加业务字段与方法(如 gvsds.apps.core.models.User)。
|
||||
"""
|
||||
|
||||
import uuid as _uuid
|
||||
@@ -186,8 +187,7 @@ class User(QModel):
|
||||
"""用户表——系统核心身份模型(抽象基类,映射 SAAS 主库 User 表标准字段)
|
||||
|
||||
本类为抽象基类,仅包含 SAAS 主库 User 表的标准字段和方法。
|
||||
业务子类应继承本类,在单独文件中添加业务字段和方法。
|
||||
参见 gvsdsdk/business_models.py。
|
||||
业务子类应继承本类并添加业务字段与方法(如 gvsds.apps.core.models.User)。
|
||||
"""
|
||||
|
||||
UserUUID = UUIDField(primary_key=True, db_column='UserUUID', verbose_name='用户UUID', default=_uuid.uuid4)
|
||||
@@ -202,6 +202,8 @@ class User(QModel):
|
||||
UserTele = models.CharField(max_length=16, null=True, blank=True, db_column='UserTele', verbose_name='电话')
|
||||
UserCreateTime = models.DateTimeField(default=_UtcNow, db_column='UserCreateTime', verbose_name='创建时间')
|
||||
UserLastLoginDate = models.DateTimeField(default=_UtcNow, db_column='UserLastLoginDate', verbose_name='最后登录')
|
||||
IsStaff = models.BooleanField(default=False, db_column='IsStaff', verbose_name='是否员工')
|
||||
IsSuperuser = models.BooleanField(default=False, db_column='IsSuperuser', verbose_name='是否超级用户')
|
||||
|
||||
# Django auth 框架必需(由具体子类覆盖)
|
||||
USERNAME_FIELD = 'UserName'
|
||||
@@ -537,11 +539,13 @@ class UserRole(QModel):
|
||||
通过 CompanyUUID 标识该用户-角色关联所属的公司,
|
||||
用于菜单按公司差异化显示、数据权限按公司过滤等场景。
|
||||
同一用户在不同公司下可绑定不同角色。
|
||||
DeptUUID、WorkgroupUUID 为可选字段,用于将关联进一步限定到部门或工作组。
|
||||
RoleType 标识该用户在部门/工作组中的角色类型:0=普通成员/部门员工,1=组长/部门负责人。
|
||||
RoleName 为可选的角色名覆盖:非空时前端展示用此名,为空时回退到 Role.RoleName。
|
||||
这避免了为每个用户单独建角色,又能让同角色在不同用户视角下显示差异化名称。
|
||||
|
||||
注意:DeptUUID、WorkgroupUUID 为遗留字段(已废弃)——部门/工作组归属已迁移到
|
||||
UserRoleDepartment/UserRoleWorkgroup 关联表,字段仅保留兼容存量数据,禁止再次写入。
|
||||
|
||||
注意:UserUUID/RoleUUID/CompanyUUID/DeptUUID/WorkgroupUUID 在 gvsds 中是 ForeignKey,
|
||||
但此处改为 UUIDField 以保持独立性(不支持 select_related 和关联属性访问)。
|
||||
"""
|
||||
@@ -719,14 +723,24 @@ class PositionTemplate(QModel):
|
||||
).values_list('PermUUID', flat=True)
|
||||
return Permission.objects.filter(PermUUID__in=perm_uuids, PermStatus=1)
|
||||
|
||||
def CopyToRole(self, RoleName, TenantUUID):
|
||||
"""从模板复制权限到新角色(一次性复制,后续模板变更不影响已有角色)"""
|
||||
def CopyToRole(self, RoleName, TenantUUID, CompanyUUID=None):
|
||||
"""从模板复制权限到新角色(一次性复制,后续模板变更不影响已有角色)
|
||||
|
||||
Args:
|
||||
RoleName: 新角色名称
|
||||
TenantUUID: 新角色所属租户
|
||||
CompanyUUID: 复制出的 RolePermission 所属公司。
|
||||
缺省用系统公司 UUID(与主项目 RolePermission 创建一致),
|
||||
RolePermission.CompanyUUID 为非空字段,漏传会导致插入失败。
|
||||
"""
|
||||
with transaction.atomic():
|
||||
role = Role.objects.create(
|
||||
RoleUUID=_uuid.uuid4().bytes,
|
||||
TenantUUID=TenantUUID,
|
||||
RoleName=RoleName,
|
||||
)
|
||||
if CompanyUUID is None:
|
||||
CompanyUUID = _uuid.UUID('00000000-0000-0000-0000-000000000001')
|
||||
template_perms = PositionTemplatePermission.objects.filter(
|
||||
PositionTemplateUUID=self.PositionTemplateUUID
|
||||
)
|
||||
@@ -734,6 +748,7 @@ class PositionTemplate(QModel):
|
||||
RolePermission(
|
||||
RolePermUUID=_uuid.uuid4().bytes,
|
||||
RoleUUID=role.RoleUUID,
|
||||
CompanyUUID=CompanyUUID,
|
||||
PermUUID=tp.PermUUID,
|
||||
)
|
||||
for tp in template_perms
|
||||
@@ -793,4 +808,4 @@ class Position(QModel):
|
||||
def __str__(self):
|
||||
return self.PositionName
|
||||
|
||||
# (UserDept 表已废弃——员工部门关联改走 UserRole.DeptUUID,不再用独立表)
|
||||
# (UserDept 表已废弃——员工部门归属已迁移到 UserRoleDepartment 关联表,不再用独立表)
|
||||
Binary file not shown.
@@ -50,12 +50,6 @@ def _ResolveMasterURL(master_url):
|
||||
return DEFAULT_MASTER_URL
|
||||
|
||||
|
||||
def _GenerateChallenge():
|
||||
"""生成认证挑战字符串——与 auth.GenerateChallenge 统一格式(时间戳:UUID)"""
|
||||
import time as _time
|
||||
return f'{_time.time():.6f}:{uuid.uuid4().hex}'
|
||||
|
||||
|
||||
class ServiceClientError(Exception):
|
||||
def __init__(self, Message, StatusCode=None, ResponseBody=None):
|
||||
super().__init__(Message)
|
||||
|
||||
Whitespace-only changes.
Reference in new issue
Block a user