You've already forked opc-backend
439 lines
18 KiB
Python
439 lines
18 KiB
Python
|
|
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)
|