You've already forked opc-backend
开发了多角色登录与鉴权接口:实现了普通用户、企业和管理员的登录分流,并支持Token验证。
开发了权限控制接口:实现了通过数据库分配菜单权限节点,控制接口访问安全。 开发了实名认证中心:实现了个人身份证信息与企业营业执照的提交与审核接口。 开发了任务与协作大厅核心业务:实现了任务的发布、接单、状态流转以及专家邀约接口。 配置了全局环境变量与数据库引擎:集成了 PostgreSQL 数据库、Redis 缓存与 MinIO 对象存储。
This commit is contained in:
438
users/serializers.py
Normal file
438
users/serializers.py
Normal file
@@ -0,0 +1,438 @@
|
||||
from rest_framework import serializers
|
||||
from .models import User, Enterprise, Role, EnterpriseMember, Permission
|
||||
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
roles = serializers.ReadOnlyField()
|
||||
permissions = serializers.SerializerMethodField()
|
||||
opc_certification = serializers.SerializerMethodField()
|
||||
enterprise_info = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['id', 'username', 'phone', 'email', 'nickname', 'avatar_url', 'face_url', 'bio', 'location', 'status', 'is_active', 'created_at', 'roles', 'permissions', 'is_staff', 'is_superuser', 'rating', 'completed_tasks', 'opc_certification', 'enterprise_info', 'is_recommended', 'recommend_priority']
|
||||
read_only_fields = ['id', 'status', 'is_active', 'created_at', 'roles', 'permissions', 'is_staff', 'is_superuser']
|
||||
|
||||
def get_enterprise_info(self, obj):
|
||||
if 'ENTERPRISE' in obj.roles:
|
||||
from .models import Enterprise, EnterpriseMember
|
||||
ent = Enterprise.objects.filter(user=obj).first()
|
||||
if ent:
|
||||
return {'company_name': ent.company_name, 'role': 'OWNER'}
|
||||
member = EnterpriseMember.objects.filter(user=obj).first()
|
||||
if member:
|
||||
return {'company_name': member.enterprise.company_name, 'role': member.role}
|
||||
return None
|
||||
|
||||
def get_opc_certification(self, obj):
|
||||
if 'OPC_USER' in obj.roles:
|
||||
from opc_cert.models import OpcCertification
|
||||
from opc_cert.serializers import OpcCertificationSerializer
|
||||
cert = OpcCertification.objects.filter(user=obj, status='APPROVED').first()
|
||||
if cert:
|
||||
return OpcCertificationSerializer(cert).data
|
||||
return None
|
||||
|
||||
def get_permissions(self, obj):
|
||||
if obj.is_superuser:
|
||||
return ['*']
|
||||
from .models import RolePermission
|
||||
perms = RolePermission.objects.filter(role__userrole__user=obj).values_list('permission__code', flat=True).distinct()
|
||||
return list(perms)
|
||||
|
||||
def validate(self, attrs):
|
||||
# Normalize empty strings to None to avoid unique constraint issues
|
||||
for field in ['phone', 'email']:
|
||||
if field in attrs and attrs[field] == '':
|
||||
attrs[field] = None
|
||||
return attrs
|
||||
|
||||
class RoleSerializer(serializers.ModelSerializer):
|
||||
permission_ids = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Role
|
||||
fields = '__all__'
|
||||
|
||||
def get_permission_ids(self, obj):
|
||||
return list(obj.role_permissions.values_list('permission_id', flat=True).distinct().values_list('permission_id', flat=True))
|
||||
|
||||
def to_representation(self, instance):
|
||||
data = super().to_representation(instance)
|
||||
# Ensure permission_ids are strings for frontend comparison
|
||||
data['permission_ids'] = [str(pid) for pid in data.get('permission_ids', [])]
|
||||
return data
|
||||
|
||||
class PermissionSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Permission
|
||||
fields = '__all__'
|
||||
|
||||
class EnterpriseMemberSerializer(serializers.ModelSerializer):
|
||||
user_details = UserSerializer(source='user', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = EnterpriseMember
|
||||
fields = ['id', 'user', 'user_details', 'role', 'joined_at']
|
||||
read_only_fields = ['id', 'joined_at']
|
||||
|
||||
|
||||
|
||||
import re
|
||||
from django.core.validators import EmailValidator
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
class RegisterSerializer(serializers.ModelSerializer):
|
||||
password = serializers.CharField(write_only=True, min_length=6)
|
||||
|
||||
username = serializers.CharField(
|
||||
error_messages={
|
||||
'unique': '该用户名已被占用,请尝试其他名称'
|
||||
}
|
||||
)
|
||||
phone = serializers.CharField(
|
||||
required=False,
|
||||
allow_blank=True,
|
||||
error_messages={
|
||||
'unique': '该手机号已注册,请直接登录'
|
||||
}
|
||||
)
|
||||
email = serializers.EmailField(
|
||||
required=False,
|
||||
allow_blank=True,
|
||||
error_messages={
|
||||
'unique': '该邮箱已注册,请尝试其他邮箱'
|
||||
}
|
||||
)
|
||||
avatar_url = serializers.CharField(required=False, allow_blank=True)
|
||||
|
||||
class Meta:
|
||||
|
||||
|
||||
model = User
|
||||
fields = ['username', 'phone', 'email', 'password', 'nickname', 'avatar_url']
|
||||
|
||||
def validate_phone(self, value):
|
||||
if value:
|
||||
if not re.match(r'^1[3-9]\d{9}$', value):
|
||||
raise ValidationError('请输入有效的11位手机号')
|
||||
if User.objects.filter(phone=value, is_deleted=False).exists():
|
||||
raise ValidationError('该手机号已注册,请直接登录')
|
||||
return value
|
||||
|
||||
def validate_email(self, value):
|
||||
if value:
|
||||
if User.objects.filter(email=value, is_deleted=False).exists():
|
||||
raise ValidationError('该邮箱已注册,请尝试其他邮箱')
|
||||
return value
|
||||
|
||||
def validate_username(self, value):
|
||||
if User.objects.filter(username=value, is_deleted=False).exists():
|
||||
raise ValidationError('该用户名已被占用,请尝试其他名称')
|
||||
return value
|
||||
|
||||
def create(self, validated_data):
|
||||
phone = validated_data.get('phone')
|
||||
if phone == '':
|
||||
phone = None
|
||||
email = validated_data.get('email')
|
||||
if email == '':
|
||||
email = None
|
||||
|
||||
user = User.objects.create_user(
|
||||
username=validated_data['username'],
|
||||
phone=phone,
|
||||
email=email,
|
||||
password=validated_data['password'],
|
||||
nickname=validated_data.get('nickname'),
|
||||
avatar_url=validated_data.get('avatar_url', '')
|
||||
)
|
||||
# Auto-assign USER role
|
||||
from .models import Role, UserRole
|
||||
user_role, _ = Role.objects.get_or_create(code='USER', defaults={'name': '普通用户'})
|
||||
UserRole.objects.get_or_create(user=user, role=user_role)
|
||||
return user
|
||||
|
||||
class AdminUserCreateSerializer(serializers.ModelSerializer):
|
||||
password = serializers.CharField(write_only=True, min_length=6)
|
||||
email = serializers.EmailField(required=False, allow_blank=True)
|
||||
username = serializers.CharField(
|
||||
error_messages={'unique': '该用户名已被占用'}
|
||||
)
|
||||
phone = serializers.CharField(
|
||||
required=False, allow_blank=True,
|
||||
error_messages={'unique': '该手机号已注册'}
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['username', 'phone', 'email', 'password', 'nickname']
|
||||
|
||||
def validate_phone(self, value):
|
||||
if value:
|
||||
if not re.match(r'^1[3-9]\d{9}$', value):
|
||||
raise ValidationError('请输入有效的11位手机号')
|
||||
if User.objects.filter(phone=value).exists():
|
||||
raise ValidationError('该手机号已注册')
|
||||
return value
|
||||
|
||||
def validate_email(self, value):
|
||||
if value:
|
||||
if User.objects.filter(email=value).exists():
|
||||
raise ValidationError('该邮箱已注册')
|
||||
return value
|
||||
|
||||
def create(self, validated_data):
|
||||
phone = validated_data.get('phone')
|
||||
if phone == '':
|
||||
phone = None
|
||||
|
||||
user = User.objects.create_user(
|
||||
username=validated_data['username'],
|
||||
phone=phone,
|
||||
email=validated_data.get('email'),
|
||||
password=validated_data['password'],
|
||||
nickname=validated_data.get('nickname')
|
||||
)
|
||||
# Auto-assign USER role
|
||||
from .models import Role, UserRole
|
||||
user_role, _ = Role.objects.get_or_create(code='USER', defaults={'name': '普通用户'})
|
||||
UserRole.objects.get_or_create(user=user, role=user_role)
|
||||
return user
|
||||
|
||||
|
||||
|
||||
class EnterpriseSerializer(serializers.ModelSerializer):
|
||||
user_details = UserSerializer(source='user', read_only=True)
|
||||
credit_code = serializers.CharField(
|
||||
error_messages={
|
||||
'unique': '该统一社会信用代码已注册,请核对信息'
|
||||
}
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Enterprise
|
||||
fields = ['id', 'user', 'user_details', 'company_name', 'credit_code', 'business_license', 'contact_name', 'contact_phone', 'landline', 'contact_email', 'address', 'description', 'status', 'logo_url', 'created_at', 'updated_at']
|
||||
read_only_fields = ['id', 'user', 'user_details', 'status', 'created_at', 'updated_at']
|
||||
|
||||
|
||||
def validate_credit_code(self, value):
|
||||
if value:
|
||||
if not re.match(r'^[A-Z0-9]{18}$', value):
|
||||
raise ValidationError('请输入有效的18位统一社会信用代码')
|
||||
return value
|
||||
|
||||
|
||||
from django.db import transaction
|
||||
|
||||
class EnterpriseRegisterSerializer(serializers.Serializer):
|
||||
email = serializers.EmailField()
|
||||
password = serializers.CharField(write_only=True, min_length=6)
|
||||
company_name = serializers.CharField(max_length=255)
|
||||
credit_code = serializers.CharField(max_length=18)
|
||||
business_license = serializers.CharField(required=False, allow_blank=True)
|
||||
logo_url = serializers.CharField(required=False, allow_blank=True)
|
||||
|
||||
def validate_credit_code(self, value):
|
||||
if not re.match(r'^[A-Z0-9]{18}$', value):
|
||||
raise ValidationError('请输入有效的18位统一社会信用代码')
|
||||
if Enterprise.objects.filter(credit_code=value).exists():
|
||||
raise ValidationError('该社会信用代码已注册')
|
||||
return value
|
||||
|
||||
def validate_email(self, value):
|
||||
if User.objects.filter(email=value).exists():
|
||||
raise ValidationError('该邮箱已注册')
|
||||
return value
|
||||
|
||||
def create(self, validated_data):
|
||||
with transaction.atomic():
|
||||
# 1. Create User
|
||||
user = User.objects.create_user(
|
||||
username=validated_data['email'],
|
||||
email=validated_data['email'],
|
||||
password=validated_data['password'],
|
||||
nickname=validated_data['company_name'],
|
||||
avatar_url=validated_data.get('logo_url')
|
||||
)
|
||||
|
||||
# 2. Create Enterprise
|
||||
enterprise = Enterprise.objects.create(
|
||||
user=user,
|
||||
company_name=validated_data['company_name'],
|
||||
credit_code=validated_data['credit_code'],
|
||||
business_license=validated_data.get('business_license', ''),
|
||||
logo_url=validated_data.get('logo_url')
|
||||
)
|
||||
|
||||
# 3. Assign Role
|
||||
from .models import Role, UserRole
|
||||
role, created = Role.objects.get_or_create(code='ENTERPRISE', defaults={'name': '企业用户'})
|
||||
UserRole.objects.get_or_create(user=user, role=role)
|
||||
|
||||
return enterprise
|
||||
|
||||
class AdminEnterpriseCreateSerializer(serializers.ModelSerializer):
|
||||
email = serializers.EmailField(write_only=True)
|
||||
password = serializers.CharField(write_only=True, min_length=6)
|
||||
|
||||
class Meta:
|
||||
model = Enterprise
|
||||
fields = ['email', 'password', 'company_name', 'credit_code', 'business_license', 'contact_name', 'contact_phone', 'contact_email', 'address', 'description', 'logo_url']
|
||||
|
||||
def validate_credit_code(self, value):
|
||||
if value:
|
||||
if not re.match(r'^[A-Z0-9]{18}$', value):
|
||||
raise ValidationError('请输入有效的18位统一社会信用代码')
|
||||
if Enterprise.objects.filter(credit_code=value).exists():
|
||||
raise ValidationError('该社会信用代码已注册')
|
||||
return value
|
||||
|
||||
def validate_email(self, value):
|
||||
if User.objects.filter(email=value).exists():
|
||||
raise ValidationError('该邮箱已注册,无法创建企业账号')
|
||||
return value
|
||||
|
||||
def create(self, validated_data):
|
||||
email = validated_data.pop('email')
|
||||
password = validated_data.pop('password')
|
||||
|
||||
with transaction.atomic():
|
||||
user = User.objects.create_user(
|
||||
username=email,
|
||||
email=email,
|
||||
password=password,
|
||||
nickname=validated_data['company_name'],
|
||||
avatar_url=validated_data.get('logo_url')
|
||||
)
|
||||
|
||||
enterprise = Enterprise.objects.create(
|
||||
user=user,
|
||||
status='VERIFIED', # Admins create pre-verified enterprises
|
||||
**validated_data
|
||||
)
|
||||
|
||||
from .models import Role, UserRole
|
||||
role, _ = Role.objects.get_or_create(code='ENTERPRISE', defaults={'name': '企业用户'})
|
||||
UserRole.objects.get_or_create(user=user, role=role)
|
||||
|
||||
return enterprise
|
||||
|
||||
from django.contrib.auth import authenticate
|
||||
|
||||
class EnterpriseLoginSerializer(serializers.Serializer):
|
||||
login_type = serializers.ChoiceField(choices=['ADMIN', 'EMPLOYEE'], default='ADMIN')
|
||||
enterprise_email = serializers.EmailField()
|
||||
username = serializers.CharField(required=False, allow_blank=True)
|
||||
password = serializers.CharField(write_only=True)
|
||||
|
||||
def validate(self, data):
|
||||
login_type = data.get('login_type', 'ADMIN')
|
||||
enterprise_email = data.get('enterprise_email')
|
||||
username = data.get('username')
|
||||
password = data.get('password')
|
||||
|
||||
# 1. Find enterprise
|
||||
try:
|
||||
enterprise_user = User.objects.get(email=enterprise_email)
|
||||
enterprise = Enterprise.objects.get(user=enterprise_user)
|
||||
except (User.DoesNotExist, Enterprise.DoesNotExist):
|
||||
raise ValidationError({'detail': '企业邮箱未注册'})
|
||||
|
||||
if login_type == 'ADMIN':
|
||||
# 2. Find and authenticate the admin
|
||||
if not enterprise_user.is_active or enterprise_user.is_deleted:
|
||||
raise ValidationError({'detail': '您的账号已被禁用,可能因为违规操作或安全风险。如有疑问请联系平台管理员。'})
|
||||
if not enterprise_user.check_password(password):
|
||||
raise ValidationError({'detail': '管理员邮箱或密码错误'})
|
||||
|
||||
# Check if user is the enterprise owner
|
||||
user = enterprise_user
|
||||
|
||||
else:
|
||||
# EMPLOYEE login
|
||||
if not username:
|
||||
raise ValidationError({'detail': '员工账号不能为空'})
|
||||
|
||||
namespaced_username = f"{enterprise_email}_{username}"
|
||||
user = User.objects.filter(username=namespaced_username).first()
|
||||
|
||||
if not user:
|
||||
raise ValidationError({'detail': '员工账号未注册'})
|
||||
if not user.is_active or user.is_deleted:
|
||||
raise ValidationError({'detail': '您的账号已被禁用,可能因为违规操作或安全风险。如有疑问请联系平台管理员。'})
|
||||
if not user.check_password(password):
|
||||
raise ValidationError({'detail': '员工账号或密码错误'})
|
||||
|
||||
# Check if user is a member of the enterprise
|
||||
if not EnterpriseMember.objects.filter(enterprise=enterprise, user=user).exists():
|
||||
raise ValidationError({'detail': '该员工不属于此企业团队'})
|
||||
|
||||
data['user'] = user
|
||||
return data
|
||||
|
||||
class EnterpriseMemberAddSerializer(serializers.Serializer):
|
||||
username = serializers.CharField()
|
||||
password = serializers.CharField(write_only=True, min_length=6)
|
||||
nickname = serializers.CharField(required=False, allow_blank=True)
|
||||
role = serializers.ChoiceField(choices=EnterpriseMember.MemberRole.choices, default=EnterpriseMember.MemberRole.MEMBER)
|
||||
enterprise_id = serializers.UUIDField(required=False)
|
||||
|
||||
def _get_enterprise(self):
|
||||
request_user = self.context['request'].user
|
||||
enterprise_id = self.initial_data.get('enterprise_id')
|
||||
|
||||
if enterprise_id and (request_user.is_staff or request_user.is_superuser):
|
||||
try:
|
||||
return Enterprise.objects.get(id=enterprise_id)
|
||||
except Enterprise.DoesNotExist:
|
||||
raise ValidationError('指定的企业不存在')
|
||||
|
||||
try:
|
||||
return Enterprise.objects.get(user=request_user)
|
||||
except Enterprise.DoesNotExist:
|
||||
raise ValidationError('当前用户没有关联的企业主体')
|
||||
|
||||
def validate_username(self, value):
|
||||
enterprise = self._get_enterprise()
|
||||
|
||||
namespaced_username = f"{enterprise.user.email}_{value}"
|
||||
if User.objects.filter(username=namespaced_username).exists():
|
||||
raise ValidationError('该用户名已被占用')
|
||||
return value
|
||||
|
||||
def create(self, validated_data):
|
||||
enterprise = self._get_enterprise()
|
||||
|
||||
with transaction.atomic():
|
||||
namespaced_username = f"{enterprise.user.email}_{validated_data['username']}"
|
||||
user = User.objects.create_user(
|
||||
username=namespaced_username,
|
||||
password=validated_data['password'],
|
||||
nickname=validated_data.get('nickname', validated_data['username'])
|
||||
)
|
||||
|
||||
# Assign ENTERPRISE role so they can access enterprise portal
|
||||
from .models import Role, UserRole
|
||||
role, _ = Role.objects.get_or_create(code='ENTERPRISE', defaults={'name': '企业用户'})
|
||||
UserRole.objects.get_or_create(user=user, role=role)
|
||||
|
||||
member = EnterpriseMember.objects.create(
|
||||
enterprise=enterprise,
|
||||
user=user,
|
||||
role=validated_data.get('role', EnterpriseMember.MemberRole.MEMBER)
|
||||
)
|
||||
|
||||
return member
|
||||
|
||||
class ChangePasswordSerializer(serializers.Serializer):
|
||||
old_password = serializers.CharField(required=True)
|
||||
new_password = serializers.CharField(required=True, min_length=6)
|
||||
|
||||
class PasswordResetRequestSerializer(serializers.Serializer):
|
||||
email = serializers.EmailField(required=True)
|
||||
|
||||
class PasswordResetConfirmSerializer(serializers.Serializer):
|
||||
email = serializers.EmailField(required=True)
|
||||
verification_code = serializers.CharField(required=True, min_length=6, max_length=6)
|
||||
new_password = serializers.CharField(required=True, min_length=6)
|
||||
Reference in New Issue
Block a user