""" Token 管理 Admin 提供 Token 的查看、搜索、过滤功能 """ from __future__ import annotations from typing import Any from django.contrib import admin from django.db.models import QuerySet from django.http import HttpRequest from django.utils import timezone from django.utils.html import format_html from django.utils.safestring import SafeString from django.utils.translation import gettext_lazy as _ from apps.automation.models import CourtToken SITE_NAME_LABELS: dict[str, str] = { "court_zxfw": "人民法院在线服务网(一张网)", "court_baoquan": "人民法院保全系统", } @admin.register(CourtToken) class CourtTokenAdmin(admin.ModelAdmin[CourtToken]): """ 一张网/保全系统 Token 管理 Admin 功能: - 查看所有一张网/保全系统 Token - 按网站、账号搜索 - 按过期状态过滤 - 显示 Token 状态(有效/过期) """ def get_model_perms(self, request: HttpRequest) -> dict[str, bool]: """隐藏 Admin 首页入口,但保留直接 URL 访问能力""" return {} list_display = [ "id", "site_name_display", "account", "token_preview", "token_type", "status_display", "expires_at", "created_at", "updated_at", ] list_filter = [ "site_name", "token_type", "created_at", "expires_at", ] search_fields = [ "site_name", "account", "token", ] readonly_fields = [ "id", "token_full", "status_display", "remaining_time", "created_at", "updated_at", ] fieldsets = ( ( _("基本信息"), { "description": _("该模块用于管理人民法院在线服务网(一张网)和保全系统的认证Token。"), "fields": ("id", "site_name", "account", "token_type"), }, ), (_("Token 信息"), {"fields": ("token_full", "status_display", "remaining_time")}), (_("时间信息"), {"fields": ("expires_at", "created_at", "updated_at")}), ) ordering = ["-created_at"] date_hierarchy = "created_at" list_per_page = 50 @admin.display(description=_("站点")) def site_name_display(self, obj: CourtToken) -> str: """显示可读站点名称""" return SITE_NAME_LABELS.get(obj.site_name, obj.site_name) # type: ignore[no-any-return] @admin.display(description=_("Token 预览")) def token_preview(self, obj: CourtToken) -> str: """Token 预览(只显示前20个字符)""" if len(obj.token) > 20: return f"{obj.token[:20]}..." return obj.token # type: ignore[no-any-return] @admin.display(description=_("完整 Token")) def token_full(self, obj: CourtToken) -> SafeString: """完整的 Token(在详情页显示)""" return format_html( '', obj.token, ) @admin.display(description=_("状态")) def status_display(self, obj: CourtToken) -> SafeString: """显示 Token 状态(有效/过期)""" if obj.is_expired(): return format_html('{}', "❌ 已过期") else: return format_html('{}', "✅ 有效") @admin.display(description=_("剩余时间")) def remaining_time(self, obj: CourtToken) -> SafeString: """剩余有效时间""" if obj.is_expired(): return format_html('{}', "已过期") now = timezone.now() remaining = obj.expires_at - now total_seconds = int(remaining.total_seconds()) hours = total_seconds // 3600 minutes = (total_seconds % 3600) // 60 seconds = total_seconds % 60 if hours > 0: time_str = f"{hours} 小时 {minutes} 分钟" elif minutes > 0: time_str = f"{minutes} 分钟 {seconds} 秒" else: time_str = f"{seconds} 秒" if total_seconds < 300: color = "red" elif total_seconds < 1800: color = "orange" else: color = "green" return format_html('{}', color, time_str) def has_add_permission(self, request: HttpRequest) -> bool: """禁用添加功能(Token 由系统自动创建)""" return False def has_change_permission(self, request: HttpRequest, obj: CourtToken | None = None) -> bool: """禁用修改功能(Token 由系统管理)""" return False def get_actions(self, request: HttpRequest) -> dict[str, Any]: """自定义批量操作""" actions = super().get_actions(request) actions["delete_expired_tokens"] = ( # type: ignore[assignment] self.delete_expired_tokens, "delete_expired_tokens", _("删除已过期的一张网/保全Token"), ) return actions def delete_expired_tokens(self, request: HttpRequest, queryset: QuerySet[CourtToken]) -> None: """批量删除过期的一张网/保全Token""" expired_tokens = [token for token in queryset if token.is_expired()] count = len(expired_tokens) for token in expired_tokens: token.delete() self.message_user(request, _(f"成功删除 {count} 个已过期的一张网/保全Token"))