开发了多角色登录与鉴权接口:实现了普通用户、企业和管理员的登录分流,并支持Token验证。

开发了权限控制接口:实现了通过数据库分配菜单权限节点,控制接口访问安全。
开发了实名认证中心:实现了个人身份证信息与企业营业执照的提交与审核接口。
开发了任务与协作大厅核心业务:实现了任务的发布、接单、状态流转以及专家邀约接口。
配置了全局环境变量与数据库引擎:集成了 PostgreSQL 数据库、Redis 缓存与 MinIO 对象存储。
This commit is contained in:
2026-04-28 16:32:02 +08:00
commit 23855ef0e4
94 changed files with 4950 additions and 0 deletions

438
users/serializers.py Normal file
View 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)