后端: - User.is_observer BooleanField (0016 migration, default=False) - AdminAuditLog 加 user_observer_toggle 操作类型 - UserSerializer fields 含 is_observer (/auth/me 透出) - IsSuperAdminOrObserver permission 类:超管 + (is_team_admin && is_observer) - 3 个 assets endpoint (overview/team_members/user_videos) 权限从 IsSuperAdmin 改为 IsSuperAdminOrObserver - admin_user_observer_toggle_view (PATCH /admin/users/<id>/observer): 仅超管,只允许打在团管上,拒超管自己 + 拒成员 - admin_users_list_view 返回 is_team_owner/is_observer 字段(前端 row-level 判断用) 前端: - User/AdminUser/TeamMember type 加 is_observer - adminApi.toggleUserObserver - ProtectedRoute 新 requireAdminOrObserver prop + requireAdmin 智能 fallback(团管被拒回 /team/dashboard) - App.tsx /admin 父路由 requireAdminOrObserver,子路由除 assets 外仍 requireAdmin (race 防御) - RoleAwareAdminIndexRedirect:观察者团管入 /admin 跳 /admin/assets,超管跳 /admin/dashboard - AdminLayout sidebar 角色过滤:观察者只见「内容资产」+ 「返回首页」改「返回团队管理」+ logo「观察者」字样 - TeamAdminLayout 观察者团管加「全局资产」入口跳 /admin/assets - AdminAssetsPage 4 处 ¥ 条件渲染 (hideMoney = role !== 'super_admin') - UsersPage 行加「设为观察者/取消观察者」按钮(仅 is_team_admin && team_id) + 观察者 badge - toast 提示「需该用户重新登录后生效」(JWT 不缓存 is_observer claim) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
57 lines
1.9 KiB
Python
57 lines
1.9 KiB
Python
from rest_framework import serializers
|
|
from django.contrib.auth import get_user_model
|
|
from rest_framework_simplejwt.tokens import RefreshToken
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
class UserSerializer(serializers.ModelSerializer):
|
|
role = serializers.CharField(read_only=True)
|
|
team_name = serializers.CharField(source='team.name', read_only=True, default=None)
|
|
|
|
class Meta:
|
|
model = User
|
|
fields = ('id', 'username', 'email', 'is_staff', 'is_team_admin', 'is_team_owner', 'is_observer', 'role', 'team_name', 'must_change_password')
|
|
|
|
|
|
class RegisterSerializer(serializers.Serializer):
|
|
username = serializers.CharField(min_length=3, max_length=20)
|
|
email = serializers.EmailField()
|
|
password = serializers.CharField(min_length=6, write_only=True)
|
|
|
|
def validate_username(self, value):
|
|
if User.objects.filter(username=value).exists():
|
|
raise serializers.ValidationError('该用户名已被注册')
|
|
return value
|
|
|
|
def validate_email(self, value):
|
|
if User.objects.filter(email=value).exists():
|
|
raise serializers.ValidationError('该邮箱已被注册')
|
|
return value
|
|
|
|
def create(self, validated_data):
|
|
user = User.objects.create_user(
|
|
username=validated_data['username'],
|
|
email=validated_data['email'],
|
|
password=validated_data['password'],
|
|
)
|
|
return user
|
|
|
|
|
|
class LoginSerializer(serializers.Serializer):
|
|
username = serializers.CharField()
|
|
password = serializers.CharField(write_only=True)
|
|
|
|
|
|
class TokenResponseSerializer(serializers.Serializer):
|
|
"""Response serializer for auth endpoints."""
|
|
user = UserSerializer()
|
|
tokens = serializers.SerializerMethodField()
|
|
|
|
def get_tokens(self, obj):
|
|
refresh = RefreshToken.for_user(obj)
|
|
return {
|
|
'access': str(refresh.access_token),
|
|
'refresh': str(refresh),
|
|
}
|