mirror of https://github.com/jumpserver/jumpserver
perf: Approval process role selection supports multiple strategies
parent
920cfdac5c
commit
41b2ce06a8
|
@ -50,13 +50,6 @@ class TicketLevel(IntegerChoices):
|
|||
two = 2, _("Two level")
|
||||
|
||||
|
||||
class TicketApprovalStrategy(TextChoices):
|
||||
org_admin = 'org_admin', _("Org admin")
|
||||
custom_user = 'custom_user', _("Custom user")
|
||||
super_admin = 'super_admin', _("Super admin")
|
||||
super_org_admin = 'super_org_admin', _("Super admin and org admin")
|
||||
|
||||
|
||||
class TicketApplyAssetScope(TextChoices):
|
||||
all = 'all', _("All assets")
|
||||
permed = 'permed', _("Permed assets")
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
# Generated by Django 4.1.13 on 2024-07-26 06:08
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
import common.db.fields
|
||||
|
||||
|
||||
def generate_user_attrs(name, match, value):
|
||||
return {
|
||||
"type": "attrs",
|
||||
"attrs": [
|
||||
{
|
||||
"name": name,
|
||||
"match": match,
|
||||
"value": value
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def migrate_assignees_to_users(apps, schema_editor):
|
||||
rule_model = apps.get_model('tickets', 'ApprovalRule')
|
||||
rules = rule_model.objects.all()
|
||||
objs = []
|
||||
|
||||
for rule in rules:
|
||||
strategy = rule.strategy
|
||||
if strategy == 'super_admin':
|
||||
rule.users = generate_user_attrs(
|
||||
"system_roles", "m2m", ["00000000-0000-0000-0000-000000000001"]
|
||||
)
|
||||
elif strategy == 'org_admin':
|
||||
rule.users = generate_user_attrs(
|
||||
"org_roles", "m2m", ["00000000-0000-0000-0000-000000000005"]
|
||||
)
|
||||
elif strategy == 'super_org_admin':
|
||||
rule.users = {
|
||||
"type": "attrs",
|
||||
"attrs": [
|
||||
{"name": "org_roles", "match": "m2m", "value": ["00000000-0000-0000-0000-000000000005"]},
|
||||
{"name": "system_roles", "match": "m2m", "value": ["00000000-0000-0000-0000-000000000001"]}
|
||||
]
|
||||
}
|
||||
elif strategy == 'custom_user':
|
||||
user_ids = [str(user_id) for user_id in rule.assignees.values_list('id', flat=True)]
|
||||
rule.users = {"type": "ids", "ids": user_ids}
|
||||
else:
|
||||
continue
|
||||
objs.append(rule)
|
||||
|
||||
rule_model.objects.bulk_update(objs, ['users'])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('tickets', '0003_initial_ticket_flow_data'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='approvalrule',
|
||||
name='users',
|
||||
field=common.db.fields.JSONManyToManyField(default=dict, to='users.User', verbose_name='Users'),
|
||||
),
|
||||
migrations.RunPython(migrate_assignees_to_users),
|
||||
migrations.RemoveField(
|
||||
model_name='approvalrule',
|
||||
name='assignees',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='approvalrule',
|
||||
name='strategy',
|
||||
),
|
||||
]
|
|
@ -3,31 +3,24 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from common.db.fields import JSONManyToManyField, RelatedManager
|
||||
from common.db.models import JMSBaseModel
|
||||
from orgs.mixins.models import OrgModelMixin
|
||||
from orgs.models import Organization
|
||||
from orgs.utils import tmp_to_org, get_current_org_id
|
||||
from orgs.utils import tmp_to_org, current_org
|
||||
from users.models import User
|
||||
from ..const import TicketType, TicketLevel, TicketApprovalStrategy
|
||||
from ..const import TicketType, TicketLevel
|
||||
|
||||
__all__ = ['TicketFlow', 'ApprovalRule']
|
||||
|
||||
|
||||
class ApprovalRule(JMSBaseModel):
|
||||
level = models.SmallIntegerField(
|
||||
default=TicketLevel.one, choices=TicketLevel.choices,
|
||||
default=TicketLevel.one,
|
||||
choices=TicketLevel.choices,
|
||||
verbose_name=_('Approve level')
|
||||
)
|
||||
strategy = models.CharField(
|
||||
max_length=64, default=TicketApprovalStrategy.super_admin,
|
||||
choices=TicketApprovalStrategy.choices,
|
||||
verbose_name=_('Approve strategy')
|
||||
)
|
||||
# 受理人列表
|
||||
assignees = models.ManyToManyField(
|
||||
'users.User', related_name='assigned_ticket_flow_approval_rule',
|
||||
verbose_name=_("Assignees")
|
||||
)
|
||||
users = JSONManyToManyField('users.User', default=dict, verbose_name=_('Users'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Ticket flow approval rule')
|
||||
|
@ -36,17 +29,10 @@ class ApprovalRule(JMSBaseModel):
|
|||
return '{}({})'.format(self.id, self.level)
|
||||
|
||||
def get_assignees(self, org_id=None):
|
||||
assignees = []
|
||||
org_id = org_id if org_id else get_current_org_id()
|
||||
with tmp_to_org(org_id):
|
||||
if self.strategy == TicketApprovalStrategy.super_admin:
|
||||
assignees = User.get_super_admins()
|
||||
elif self.strategy == TicketApprovalStrategy.org_admin:
|
||||
assignees = User.get_org_admins()
|
||||
elif self.strategy == TicketApprovalStrategy.super_org_admin:
|
||||
assignees = User.get_super_and_org_admins()
|
||||
elif self.strategy == TicketApprovalStrategy.custom_user:
|
||||
assignees = self.assignees.all()
|
||||
org = Organization.get_instance(org_id, default=current_org)
|
||||
user_qs = User.get_org_users(org=org)
|
||||
query = RelatedManager.get_to_filter_qs(self.users.value, user_qs.model)
|
||||
assignees = user_qs.filter(*query).distinct()
|
||||
return assignees
|
||||
|
||||
|
||||
|
|
|
@ -1,51 +1,23 @@
|
|||
from django.db.transaction import atomic
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from common.serializers.fields import LabeledChoiceField
|
||||
from common.serializers.fields import LabeledChoiceField, JSONManyToManyField
|
||||
from orgs.mixins.serializers import OrgResourceModelSerializerMixin
|
||||
from orgs.models import Organization
|
||||
from orgs.utils import get_current_org_id
|
||||
from tickets.const import TicketApprovalStrategy, TicketType
|
||||
from tickets.const import TicketType
|
||||
from tickets.models import TicketFlow, ApprovalRule
|
||||
|
||||
__all__ = ['TicketFlowSerializer']
|
||||
|
||||
|
||||
class TicketFlowApproveSerializer(serializers.ModelSerializer):
|
||||
strategy = LabeledChoiceField(
|
||||
choices=TicketApprovalStrategy.choices, required=True, label=_('Approve strategy')
|
||||
)
|
||||
assignees_read_only = serializers.SerializerMethodField(label=_('Assignees'))
|
||||
assignees_display = serializers.SerializerMethodField(label=_('Assignees display'))
|
||||
users = JSONManyToManyField(label=_('User'))
|
||||
|
||||
class Meta:
|
||||
model = ApprovalRule
|
||||
fields_small = [
|
||||
'level', 'strategy', 'assignees_read_only', 'assignees_display',
|
||||
]
|
||||
fields_m2m = ['assignees', ]
|
||||
fields = fields_small + fields_m2m
|
||||
read_only_fields = ['level', 'assignees_display']
|
||||
extra_kwargs = {
|
||||
'assignees': {'write_only': True, 'allow_empty': True, 'required': False}
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_assignees_display(instance):
|
||||
return [str(assignee) for assignee in instance.get_assignees()]
|
||||
|
||||
@staticmethod
|
||||
def get_assignees_read_only(instance):
|
||||
if instance.strategy == TicketApprovalStrategy.custom_user:
|
||||
return instance.assignees.values_list('id', flat=True)
|
||||
return []
|
||||
|
||||
def validate(self, attrs):
|
||||
if attrs['strategy'] == TicketApprovalStrategy.custom_user and not attrs.get('assignees'):
|
||||
error = _('Please select the Assignees')
|
||||
raise serializers.ValidationError({'assignees': error})
|
||||
return super().validate(attrs)
|
||||
fields = ['level', 'users']
|
||||
read_only_fields = ['level']
|
||||
|
||||
|
||||
class TicketFlowSerializer(OrgResourceModelSerializerMixin):
|
||||
|
@ -56,15 +28,14 @@ class TicketFlowSerializer(OrgResourceModelSerializerMixin):
|
|||
|
||||
class Meta:
|
||||
model = TicketFlow
|
||||
fields_mini = ['id', ]
|
||||
fields_mini = ['id', 'type']
|
||||
fields_small = fields_mini + [
|
||||
'type', 'approval_level', 'created_by', 'date_created', 'date_updated',
|
||||
'org_id', 'org_name'
|
||||
'approval_level', 'created_by', 'date_created',
|
||||
'date_updated', 'org_id', 'org_name'
|
||||
]
|
||||
fields = fields_small + ['rules', ]
|
||||
read_only_fields = ['created_by', 'org_id', 'date_created', 'date_updated']
|
||||
fields = fields_small + ['rules']
|
||||
read_only_fields = ['created_by', 'date_created', 'date_updated']
|
||||
extra_kwargs = {
|
||||
'type': {'required': True},
|
||||
'approval_level': {'required': True}
|
||||
}
|
||||
|
||||
|
@ -76,31 +47,23 @@ class TicketFlowSerializer(OrgResourceModelSerializerMixin):
|
|||
return value
|
||||
|
||||
def create_or_update(self, action, validated_data, instance=None):
|
||||
related = 'rules'
|
||||
assignees = 'assignees'
|
||||
childs = validated_data.pop(related, [])
|
||||
if not instance:
|
||||
children = validated_data.pop('rules', [])
|
||||
if instance is None:
|
||||
instance = getattr(super(), action)(validated_data)
|
||||
else:
|
||||
instance = getattr(super(), action)(instance, validated_data)
|
||||
getattr(instance, related).all().delete()
|
||||
instance_related = getattr(instance, related)
|
||||
child_instances = []
|
||||
related_model = instance_related.model
|
||||
# Todo: 这个权限的判断
|
||||
for level, data in enumerate(childs, 1):
|
||||
data_m2m = data.pop(assignees, None)
|
||||
child_instance = related_model.objects.create(**data, level=level)
|
||||
getattr(child_instance, assignees).set(data_m2m)
|
||||
child_instances.append(child_instance)
|
||||
instance_related.set(child_instances)
|
||||
instance.rules.all().delete()
|
||||
|
||||
child_instances = [
|
||||
instance.rules.model.objects.create(**data, level=level)
|
||||
for level, data in enumerate(children, 1)
|
||||
]
|
||||
instance.rules.set(child_instances)
|
||||
return instance
|
||||
|
||||
@atomic
|
||||
def create(self, validated_data):
|
||||
return self.create_or_update('create', validated_data)
|
||||
|
||||
@atomic
|
||||
def update(self, instance, validated_data):
|
||||
current_org_id = get_current_org_id()
|
||||
root_org_id = Organization.ROOT_ID
|
||||
|
|
Loading…
Reference in New Issue