diff --git a/apps/accounts/filters.py b/apps/accounts/filters.py index fa29bcbfc..67e243e1c 100644 --- a/apps/accounts/filters.py +++ b/apps/accounts/filters.py @@ -5,7 +5,6 @@ from django_filters import rest_framework as drf_filters from assets.models import Node from common.drf.filters import BaseFilterSet - from .models import Account, GatheredAccount @@ -46,7 +45,7 @@ class AccountFilterSet(BaseFilterSet): class Meta: model = Account - fields = ['id', 'asset_id'] + fields = ['id', 'asset_id', 'source_id'] class GatheredAccountFilterSet(BaseFilterSet): diff --git a/apps/accounts/models/account.py b/apps/accounts/models/account.py index 4094018e1..de89db545 100644 --- a/apps/accounts/models/account.py +++ b/apps/accounts/models/account.py @@ -1,4 +1,6 @@ from django.db import models +from django.db.models import Count +from django.utils import timezone from django.utils.translation import gettext_lazy as _ from simple_history.models import HistoricalRecords @@ -118,3 +120,45 @@ class AccountTemplate(BaseAccount): def __str__(self): return self.username + + @staticmethod + def bulk_update_accounts(accounts, data): + history_model = Account.history.model + account_ids = accounts.values_list('id', flat=True) + history_accounts = history_model.objects.filter(id__in=account_ids) + account_id_count_map = { + str(i['id']): i['count'] + for i in history_accounts.values('id').order_by('id') + .annotate(count=Count(1)).values('id', 'count') + } + + for account in accounts: + account_id = str(account.id) + account.version = account_id_count_map.get(account_id) + 1 + for k, v in data.items(): + setattr(account, k, v) + Account.objects.bulk_update(accounts, ['version', 'secret']) + + @staticmethod + def bulk_create_history_accounts(accounts, user_id): + history_model = Account.history.model + history_account_objs = [] + for account in accounts: + history_account_objs.append( + history_model( + id=account.id, + version=account.version, + secret=account.secret, + secret_type=account.secret_type, + history_user_id=user_id, + history_date=timezone.now() + ) + ) + history_model.objects.bulk_create(history_account_objs) + + def bulk_sync_account_secret(self, accounts, user_id): + """ 批量同步账号密码 """ + if not accounts: + return + self.bulk_update_accounts(accounts, {'secret': self.secret}) + self.bulk_create_history_accounts(accounts, user_id) diff --git a/apps/accounts/serializers/account/template.py b/apps/accounts/serializers/account/template.py index 1de4d7c1d..bbf83bd89 100644 --- a/apps/accounts/serializers/account/template.py +++ b/apps/accounts/serializers/account/template.py @@ -1,86 +1,37 @@ -from django.db.transaction import atomic -from django.db.utils import IntegrityError +from rest_framework import serializers from accounts.models import AccountTemplate, Account -from assets.models import Asset from common.serializers import SecretReadableMixin from .base import BaseAccountSerializer class AccountTemplateSerializer(BaseAccountSerializer): + is_sync_account = serializers.BooleanField(default=False, write_only=True) + _is_sync_account = False + class Meta(BaseAccountSerializer.Meta): model = AccountTemplate + fields = BaseAccountSerializer.Meta.fields + ['is_sync_account'] - @staticmethod - def account_save(data, account): - for field, value in data.items(): - setattr(account, field, value) - try: - account.save(update_fields=list(data.keys())) - except IntegrityError: - pass - - # TODO 数据库访问的太多了 后期优化 - @atomic() - def bulk_update_accounts(self, instance, diff): - accounts = Account.objects.filter(source_id=instance.id) - if not accounts: + def sync_accounts_secret(self, instance, diff): + if not self._is_sync_account or 'secret' not in diff: return - diff.pop('secret', None) - name = diff.pop('name', None) - username = diff.pop('username', None) - secret_type = diff.pop('secret_type', None) - update_accounts = [] - for account in accounts: - for field, value in diff.items(): - setattr(account, field, value) - update_accounts.append(account) + accounts = Account.objects.filter(source_id=instance.id) + instance.bulk_sync_account_secret(accounts, self.context['request'].user.id) - if update_accounts: - Account.objects.bulk_update(update_accounts, diff.keys()) - - if name: - for account in accounts: - data = {'name': name} - self.account_save(data, account) - - if secret_type and username: - asset_ids_supports = self.get_asset_ids_supports(accounts, secret_type) - for account in accounts: - asset_id = account.asset_id - if asset_id not in asset_ids_supports: - data = {'username': username} - self.account_save(data, account) - continue - data = {'username': username, 'secret_type': secret_type, 'secret': instance.secret} - self.account_save(data, account) - elif secret_type: - asset_ids_supports = self.get_asset_ids_supports(accounts, secret_type) - for account in accounts: - asset_id = account.asset_id - if asset_id not in asset_ids_supports: - continue - data = {'secret_type': secret_type, 'secret': instance.secret} - self.account_save(data, account) - elif username: - for account in accounts: - data = {'username': username} - self.account_save(data, account) - - @staticmethod - def get_asset_ids_supports(accounts, secret_type): - asset_ids = accounts.values_list('asset_id', flat=True) - secret_type_supports = Asset.get_secret_type_assets(asset_ids, secret_type) - return [asset.id for asset in secret_type_supports] + def validate(self, attrs): + self._is_sync_account = attrs.pop('is_sync_account', None) + attrs = super().validate(attrs) + return attrs def update(self, instance, validated_data): - # diff = { - # k: v for k, v in validated_data.items() - # if getattr(instance, k) != v - # } + diff = { + k: v for k, v in validated_data.items() + if getattr(instance, k) != v + } instance = super().update(instance, validated_data) - # self.bulk_update_accounts(instance, diff) + self.sync_accounts_secret(instance, diff) return instance