feat: add admin client policy control

This commit is contained in:
QiuSW
2026-07-28 15:42:23 +08:00
parent 9544aaf4cc
commit 2171968780
17 changed files with 558 additions and 21 deletions
+57
View File
@@ -11,7 +11,13 @@ from django.contrib.auth import get_user_model
from django.db import IntegrityError, transaction
from django.utils import timezone
from config.client_subscription_policy import (
build_client_subscription_policy,
get_client_subscription_policy,
)
from apps.licensing.models import (
ClientSubscriptionPolicy,
ClientSubscriptionPolicyAudit,
ClientDevice,
DeviceCredential,
DeviceBindingAudit,
@@ -289,6 +295,57 @@ def _required_reason(reason: str) -> str:
return normalized_reason
def get_published_client_subscription_policy() -> dict:
"""Return the admin override when present, otherwise the T-635 env fallback."""
policy = ClientSubscriptionPolicy.objects.filter(pk=1).only("mode", "updated_at").first()
if policy is None:
return get_client_subscription_policy()
return build_client_subscription_policy(mode=policy.mode, updated_at=policy.updated_at)
@transaction.atomic
def set_client_subscription_policy(*, mode: str, reason: str, actor) -> ClientSubscriptionPolicy:
"""Persist one audited policy transition without allowing a second singleton."""
normalized_mode = str(mode or "").strip().lower()
if normalized_mode not in ClientSubscriptionPolicy.Mode.values:
raise LicensingError("bad_request", "客户端订阅策略不合法")
normalized_reason = _required_reason(reason)
policy = ClientSubscriptionPolicy.objects.select_for_update().filter(pk=1).first()
created = policy is None
if policy is None:
try:
with transaction.atomic():
policy = ClientSubscriptionPolicy.objects.create(
singleton_id=1,
mode=normalized_mode,
updated_by=actor,
)
except IntegrityError:
policy = ClientSubscriptionPolicy.objects.select_for_update().get(pk=1)
created = False
if not created and policy.mode == normalized_mode:
raise LicensingError("policy_unchanged", "请选择不同的客户端订阅策略")
previous_mode = "" if created else policy.mode
if not created:
policy.mode = normalized_mode
policy.updated_by = actor
policy.save(update_fields=("mode", "updated_by", "updated_at"))
ClientSubscriptionPolicyAudit.objects.create(
policy=policy,
previous_mode=previous_mode,
mode=normalized_mode,
reason=normalized_reason,
changed_by=actor,
)
return policy
def _create_license_event(
*,
entitlement: SoftwareEntitlement,