diff --git a/auth/__pycache__/managers.cpython-312.pyc b/auth/__pycache__/managers.cpython-312.pyc index 5d42386..105dff0 100644 Binary files a/auth/__pycache__/managers.cpython-312.pyc and b/auth/__pycache__/managers.cpython-312.pyc differ diff --git a/auth/managers.py b/auth/managers.py index 8267fef..57fdf4e 100644 --- a/auth/managers.py +++ b/auth/managers.py @@ -1,17 +1,17 @@ import uuid from django.utils import timezone as _timezone -from gvsdsdk.models import Role, UserRole, RolePermission, Permission +from gvsdsdk.models import Role, UserRole, RolePermission, Permission, Position, PositionTemplate, PositionTemplatePermission from gvsdsdk.model_utils import _GetUserModel -# NOTE: 类型注解暂缓——RoleManager 静态方法较多,全量补充注解工作量较大且易引入回归,后续单独迭代处理。 +# NOTE: 类型注解暂缓——RoleManager/PositionManager 静态方法较多,全量补充注解工作量较大且易引入回归,后续单独迭代处理。 class RoleManager: """角色管理器——提供角色创建、分配、权限管理""" @staticmethod - def CreateRole(Name, TenantUUID, AssignScope=0, AssignScopeUUID=None, RoleType=2): + def CreateRole(Name, TenantUUID, AssignScope=0, AssignScopeUUID=None, RoleType=2, RoleDesc=''): """创建角色""" role = Role( RoleUUID=uuid.uuid4().bytes, @@ -21,12 +21,17 @@ class RoleManager: RoleStatus=1, AssignScope=AssignScope, AssignScopeUUID=AssignScopeUUID, - RoleDesc='', + RoleDesc=RoleDesc, CreateTime=_timezone.now(), ) role.save() return role + @staticmethod + def GetRoleByUUID(RoleUUID): + """按 UUID 查询角色(不存在返回 None)""" + return Role.objects.filter(RoleUUID=RoleUUID).first() + @staticmethod def GetRoleByName(Name, TenantUUID=None): """按名称查询角色(同租户内角色名唯一) @@ -138,3 +143,199 @@ class RoleManager: perm_uuids = qs.values_list('PermUUID', flat=True) return Permission.objects.filter(PermUUID__in=perm_uuids, PermStatus=1) + @staticmethod + def GetUserRoleDetails(UserUUID, CompanyUUID=None): + """获取用户的 UserRole 关联详情(按行返回,保留行级信息) + + 与 GetRolesForUser(仅返回 Role)互补:本方法保留 UserRole 的 + CompanyUUID、RoleName 覆盖、RoleType、UserRoleUUID 等行级字段, + 供需要展示"角色+公司+覆盖名"等富信息的场景使用。 + + Returns: + list[UserRole]:UserRole 实例列表(保持数据库顺序) + """ + qs = UserRole.objects.filter(UserUUID=UserUUID) + if CompanyUUID is not None: + qs = qs.filter(CompanyUUID=CompanyUUID) + return list(qs) + + @staticmethod + def HasRolePermission(RoleUUID, PermUUID, CompanyUUID=None): + """检查角色是否已拥有指定权限(同公司下防重复绑定)""" + qs = RolePermission.objects.filter(RoleUUID=RoleUUID, PermUUID=PermUUID) + if CompanyUUID is not None: + qs = qs.filter(CompanyUUID=CompanyUUID) + return qs.exists() + + +class PositionManager: + """职务管理器——提供人事职务标签创建、岗位模板管理""" + + # ── 人事职务标签 ── + + @staticmethod + def CreatePosition(Name, TenantUUID, Code='', Level=0, **Kwargs): + """创建人事职务标签(TenantUUID 传 UUIDObj/str/bytes)""" + 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 GetPositionByUUID(PositionUUID): + """按 UUID 查询职务(不存在返回 None)""" + return Position.objects.filter(PositionUUID=PositionUUID).first() + + @staticmethod + def UpdatePosition(PositionUUID, **Fields): + """更新职务字段(自动刷新 UpdateTime) + + Args: + PositionUUID: 职务 UUID + **Fields: 允许字段 PositionName/PositionCode/PositionLevel/PositionStatus/PositionDesc + + Returns: + 更新后的 Position 实例;职务不存在返回 None + """ + position = Position.objects.filter(PositionUUID=PositionUUID).first() + if not position: + return None + allowed = ('PositionName', 'PositionCode', 'PositionLevel', 'PositionStatus', 'PositionDesc') + update_fields = [] + for key, value in Fields.items(): + if key in allowed: + setattr(position, key, value) + update_fields.append(key) + position.UpdateTime = _timezone.now() + update_fields.append('UpdateTime') + if update_fields: + position.save(update_fields=update_fields) + return position + + @staticmethod + def DeletePosition(PositionUUID): + """软删除职务(PositionStatus=0);返回是否命中""" + position = Position.objects.filter(PositionUUID=PositionUUID).first() + if not position: + return False + position.PositionStatus = 0 + position.UpdateTime = _timezone.now() + position.save(update_fields=['PositionStatus', 'UpdateTime']) + return True + + # ── 岗位模板 ── + + @staticmethod + def CreatePositionTemplate(Name, TenantUUID, Code='', **Kwargs): + """创建岗位模板(TenantUUID 传 UUIDObj/str/bytes)""" + 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 GetTemplatesForTenant(TenantUUID): + """获取租户下的所有启用岗位模板""" + return PositionTemplate.objects.filter(TenantUUID=TenantUUID, TemplateStatus=1) + + @staticmethod + def GetPositionTemplateByUUID(PositionTemplateUUID): + """按 UUID 查询岗位模板(不存在返回 None)""" + return PositionTemplate.objects.filter(PositionTemplateUUID=PositionTemplateUUID).first() + + @staticmethod + def UpdatePositionTemplate(PositionTemplateUUID, **Fields): + """更新岗位模板字段(自动刷新 UpdateTime) + + Args: + PositionTemplateUUID: 岗位模板 UUID + **Fields: 允许字段 TemplateName/TemplateCode/TemplateStatus/TemplateDesc + + Returns: + 更新后的 PositionTemplate 实例;模板不存在返回 None + """ + template = PositionTemplate.objects.filter(PositionTemplateUUID=PositionTemplateUUID).first() + if not template: + return None + allowed = ('TemplateName', 'TemplateCode', 'TemplateStatus', 'TemplateDesc') + update_fields = [] + for key, value in Fields.items(): + if key in allowed: + setattr(template, key, value) + update_fields.append(key) + template.UpdateTime = _timezone.now() + update_fields.append('UpdateTime') + if update_fields: + template.save(update_fields=update_fields) + return template + + @staticmethod + def DeletePositionTemplate(PositionTemplateUUID): + """软删除岗位模板(TemplateStatus=0);返回是否命中""" + template = PositionTemplate.objects.filter(PositionTemplateUUID=PositionTemplateUUID).first() + if not template: + return False + template.TemplateStatus = 0 + template.UpdateTime = _timezone.now() + template.save(update_fields=['TemplateStatus', 'UpdateTime']) + return True + + # ── 岗位模板权限关联 ── + + @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 HasTemplatePerm(TemplateUUID, PermUUID): + """检查岗位模板是否已关联指定权限""" + return PositionTemplatePermission.objects.filter( + PositionTemplateUUID=TemplateUUID, PermUUID=PermUUID, + ).exists() + diff --git a/commission/__pycache__/manager.cpython-312.pyc b/commission/__pycache__/manager.cpython-312.pyc index 4dadfd0..48f3df3 100644 Binary files a/commission/__pycache__/manager.cpython-312.pyc and b/commission/__pycache__/manager.cpython-312.pyc differ diff --git a/commission/__pycache__/models.cpython-312.pyc b/commission/__pycache__/models.cpython-312.pyc index 68483a1..68aa234 100644 Binary files a/commission/__pycache__/models.cpython-312.pyc and b/commission/__pycache__/models.cpython-312.pyc differ diff --git a/commission/manager.py b/commission/manager.py index 6fe2189..8c5fc1a 100644 --- a/commission/manager.py +++ b/commission/manager.py @@ -166,6 +166,54 @@ class CommissionEngine: logger.info(f'费率表已创建: {table_code}@tenant={self.TenantUUID}') return table + def ListRules(self, event_type=None): + """列出租户下的分佣规则(仅启用,按优先级降序) + + Args: + event_type: 可选,按事件类型过滤 + + Returns: + QuerySet[CommissionRule] + """ + qs = CommissionRule.objects.filter( + TenantUUID=self.TenantUUID, RuleStatus=1, + ) + if event_type: + qs = qs.filter(EventType=event_type) + return qs.order_by('-RulePriority') + + def DisableRule(self, rule_uuid): + """软禁用分佣规则(RuleStatus=0);返回是否命中""" + updated = CommissionRule.objects.filter( + TenantUUID=self.TenantUUID, RuleUUID=rule_uuid, + ).update(RuleStatus=0) + return updated > 0 + + def ListRateTables(self): + """列出租户下的费率表(仅启用) + + Returns: + QuerySet[RateTable] + """ + return RateTable.objects.filter( + TenantUUID=self.TenantUUID, TableStatus=1, + ) + + def ListEvents(self, event_type=None, limit=50): + """列出租户下的分佣事件(按创建时间倒序) + + Args: + event_type: 可选,按事件类型过滤 + limit: 返回条数上限 + + Returns: + QuerySet[CommissionEvent] + """ + qs = CommissionEvent.objects.filter(TenantUUID=self.TenantUUID) + if event_type: + qs = qs.filter(EventType=event_type) + return qs.order_by('-CreateTime')[:limit] + def Dispatch(self, event_type, event_key, total_amount, source_user_uuid=None, participants=None, tier_index=0, payload=None, auto_settle=True): diff --git a/payment/__pycache__/manager.cpython-312.pyc b/payment/__pycache__/manager.cpython-312.pyc index 740bea7..ed55b5d 100644 Binary files a/payment/__pycache__/manager.cpython-312.pyc and b/payment/__pycache__/manager.cpython-312.pyc differ diff --git a/payment/manager.py b/payment/manager.py index 8f45ed9..6527de6 100644 --- a/payment/manager.py +++ b/payment/manager.py @@ -160,6 +160,49 @@ class PaymentManager: logger.info(f'支付网关配置: {platform}@tenant={self.TenantUUID}') return config + def ListConfigs(self, status=None): + """列出租户下的支付网关配置(默认按默认渠道优先、平台名排序) + + Args: + status: 可选,按配置状态过滤(1=启用,0=禁用) + + Returns: + QuerySet[PaymentGatewayConfig] + """ + qs = PaymentGatewayConfig.objects.filter( + TenantUUID=self.TenantUUID, + ) + if status is not None: + qs = qs.filter(ConfigStatus=status) + return qs.order_by('-ConfigDefault', 'Platform') + + def GetConfigByUUID(self, config_uuid): + """按 ConfigUUID 查询租户下的配置(不存在返回 None)""" + return PaymentGatewayConfig.objects.filter( + ConfigUUID=config_uuid, TenantUUID=self.TenantUUID, + ).first() + + def DisableConfig(self, platform): + """禁用租户指定平台的配置(ConfigStatus=0 并清空更新时间);返回是否命中""" + updated = PaymentGatewayConfig.objects.filter( + TenantUUID=self.TenantUUID, Platform=platform, + ).update(ConfigStatus=0, UpdateTime=None) + return updated > 0 + + def SetConfigStatus(self, config_uuid, status): + """更新租户指定配置的状态;返回是否命中""" + updated = PaymentGatewayConfig.objects.filter( + ConfigUUID=config_uuid, TenantUUID=self.TenantUUID, + ).update(ConfigStatus=status) + return updated > 0 + + def DeleteConfig(self, config_uuid): + """删除租户指定配置;返回是否命中""" + deleted, _ = PaymentGatewayConfig.objects.filter( + ConfigUUID=config_uuid, TenantUUID=self.TenantUUID, + ).delete() + return deleted > 0 + def Pay(self, platform, amount, subject='', body='', method='jsapi', open_id='', client_ip='', extra=None, expire_minutes=30):