"feat(managers): extend commission/payment/position managers

- CommissionEngine: ListRules/DisableRule/ListRateTables/ListEvents
- PaymentManager: ListConfigs/GetConfigByUUID/DisableConfig/SetConfigStatus/DeleteConfig
- PositionManager: HasTemplatePerm"
This commit is contained in:
TermiNexus committed 2026-08-07 02:26:19 +08:00
1 parent e9bde4db76
commit f6842ee3c8
7 files changed
+296 -4

No files matched your search

Binary file not shown.
+205 -4
View File
@@ -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()
Binary file not shown.
Binary file not shown.
+48
View File
@@ -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):
Binary file not shown.
+43
View File
@@ -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):