feat(admin): Phase 0 平台超管基础 — is_platform_admin + IsPlatformAdmin + create_platform_admin(admin/admin123) + AdminAuditLog + Admin 后台外壳与路由 gating

后端:User.is_platform_admin + migration;权限类 IsPlatformAdmin;管理命令建 admin/admin123(幂等);
AdminAuditLog 模型 + log_admin_action() helper;me/login 对无团队超管优雅返回 team=null;UserSerializer 暴露标志。
前端:routes/admin 后台外壳(分组侧栏 + 概览 + 占位)、/admin 路由解析与 gating(超管直落、非超管纠回)、
侧栏平台入口、admin-page.css(仅 token)、IconKitSvg 补图标。
测试:accounts 11/11 单测过;无头 e2e _admin-p0.mjs 全断言过 + 0 console error;tsc+build 绿。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
seaislee1209
2026-06-19 20:49:34 +08:00
co-authored by Claude Opus 4.8
parent 8fc3870fa3
commit 443023a1a9
21 changed files with 907 additions and 12 deletions
@@ -0,0 +1,61 @@
"""创建/确保平台超级管理员账号。
默认 admin / admin123(本期定调:用户第一个超管)。平台超管不属于任何团队 —— 登录后
前端直落 /admin 后台。重复执行幂等:已存在则只补 is_platform_admin 标志(可选 --reset-password 重置密码)。
python manage.py create_platform_admin
python manage.py create_platform_admin --username admin --password admin123 --reset-password
"""
from django.core.management.base import BaseCommand
from django.db import transaction
class Command(BaseCommand):
help = "创建或确保平台超级管理员(默认 admin/admin123)"
def add_arguments(self, parser):
parser.add_argument("--username", default="admin", help="超管用户名(默认 admin)")
parser.add_argument("--password", default="admin123", help="超管密码(默认 admin123)")
parser.add_argument(
"--reset-password",
action="store_true",
help="账号已存在时也强制重置为给定密码",
)
@transaction.atomic
def handle(self, *args, **options):
from apps.accounts.models import User
username = options["username"]
password = options["password"]
reset = options["reset_password"]
user, created = User.objects.get_or_create(
username=username,
defaults={"email": f"{username}@airshelf.local"},
)
changed = []
if not user.is_platform_admin:
user.is_platform_admin = True
changed.append("is_platform_admin")
# 也给 Django staff 标志,便于需要时进 Django admin;不影响业务权限(业务只看 is_platform_admin)
if not user.is_staff:
user.is_staff = True
changed.append("is_staff")
if user.status != User.Status.ACTIVE:
user.status = User.Status.ACTIVE
changed.append("status")
if created or reset:
user.set_password(password)
changed.append("password")
user.save()
if created:
self.stdout.write(self.style.SUCCESS(f"已创建平台超管 {username}(密码 {password})"))
elif changed:
self.stdout.write(
self.style.SUCCESS(f"平台超管 {username} 已更新:{', '.join(changed)}")
)
else:
self.stdout.write(self.style.WARNING(f"平台超管 {username} 已存在,无需变更"))