feat: 添加邮箱验证码登录功能
This commit is contained in:
parent
32dcbf97a5
commit
dbdc0086d2
|
|
@ -0,0 +1,41 @@
|
|||
from rest_framework.generics import CreateAPIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.exceptions import ParseError
|
||||
from apps.auth1.serializers import EmailCodeSerializer
|
||||
from django.core.cache import cache
|
||||
from apps.utils.tools import rannum
|
||||
|
||||
|
||||
class EmailCodeView(CreateAPIView):
|
||||
"""发送邮箱验证码
|
||||
|
||||
发送邮箱验证码用于注册
|
||||
"""
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
serializer_class = EmailCodeSerializer
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
vdata = serializer.validated_data
|
||||
email = vdata.get('email')
|
||||
|
||||
cache_key = f'email_code_{email}'
|
||||
|
||||
# 生成6位验证码
|
||||
code = rannum(6)
|
||||
print(code)
|
||||
|
||||
# 缓存验证码,有效期5分钟
|
||||
cache.set(cache_key, code, 60 * 5)
|
||||
|
||||
# 使用 send_mail_task 发送验证码邮件
|
||||
from apps.utils.tasks import send_mail_task
|
||||
send_mail_task.delay(
|
||||
subject='登录验证码',
|
||||
message=f'您的验证码是:{code},5分钟内有效。如非本人操作,请忽略此邮件。',
|
||||
recipient_list=[email]
|
||||
)
|
||||
|
||||
return Response({'message': '验证码已发送'})
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
from rest_framework.generics import CreateAPIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from rest_framework.exceptions import ParseError
|
||||
from apps.auth1.serializers import RegisterSerializer
|
||||
from apps.system.models import User
|
||||
from django.db import transaction
|
||||
|
||||
|
||||
class RegisterView(CreateAPIView):
|
||||
"""用户注册
|
||||
|
||||
用户注册,创建用户和企业信息
|
||||
"""
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
serializer_class = RegisterSerializer
|
||||
|
||||
@transaction.atomic
|
||||
def create(self, request, *args, **kwargs):
|
||||
from apps.carbon.models import Enterprise
|
||||
import re
|
||||
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
vdata = serializer.validated_data
|
||||
|
||||
email = vdata.get('email')
|
||||
code = vdata.get('code')
|
||||
nickname = vdata.get('nickname')
|
||||
password = vdata.get('password')
|
||||
enterprise_name = vdata.get('enterprise_name')
|
||||
credit_code = vdata.get('credit_code')
|
||||
|
||||
# 验证邮箱验证码
|
||||
from django.core.cache import cache
|
||||
cache_key = f'email_code_{email}'
|
||||
cached_code = cache.get(cache_key)
|
||||
if not cached_code or cached_code != code:
|
||||
raise ParseError('验证码错误或已过期')
|
||||
|
||||
# 验证统一社会信用代码格式
|
||||
credit_code_pattern = r'^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$'
|
||||
if not re.match(credit_code_pattern, credit_code):
|
||||
raise ParseError('统一社会信用代码格式不正确')
|
||||
|
||||
# 检查邮箱是否已存在
|
||||
if User.objects.filter(username=email).exists():
|
||||
raise ParseError('该邮箱已注册')
|
||||
|
||||
# 检查统一社会信用代码是否已存在
|
||||
if Enterprise.objects.filter(credit_code=credit_code).exists():
|
||||
raise ParseError('该统一社会信用代码已注册')
|
||||
|
||||
# 创建企业部门
|
||||
from apps.system.models import Dept
|
||||
# 获取根组织部门
|
||||
parent_dept = Dept.objects.get(name='根组织')
|
||||
dept = Dept.objects.create(
|
||||
name=enterprise_name,
|
||||
type='enterprise',
|
||||
parent=parent_dept
|
||||
)
|
||||
|
||||
# 创建企业信息
|
||||
enterprise = Enterprise.objects.create(
|
||||
name=enterprise_name,
|
||||
credit_code=credit_code,
|
||||
belong_dept=dept
|
||||
)
|
||||
|
||||
# 创建用户
|
||||
user = User.objects.create_user(
|
||||
username=email,
|
||||
password=password,
|
||||
type='enterprise',
|
||||
name=nickname
|
||||
)
|
||||
|
||||
# 授予权限
|
||||
from apps.system.models import UserPost, Post
|
||||
post = Post.objects.get(name='核算员')
|
||||
UserPost.objects.create(
|
||||
user = user,
|
||||
post = post,
|
||||
dept = dept,
|
||||
sort = 1
|
||||
)
|
||||
user.belong_dept = dept
|
||||
user.post = post
|
||||
user.save(update_fields=['belong_dept', 'post'])
|
||||
|
||||
return Response({
|
||||
'message': '注册成功',
|
||||
'user_id': user.id,
|
||||
'enterprise_id': enterprise.id
|
||||
}, status=status.HTTP_201_CREATED)
|
||||
|
|
@ -32,4 +32,22 @@ class SecretLoginSerializer(serializers.Serializer):
|
|||
|
||||
|
||||
class FaceLoginSerializer(serializers.Serializer):
|
||||
base64 = serializers.CharField()
|
||||
base64 = serializers.CharField()
|
||||
|
||||
|
||||
class EmailCodeSerializer(serializers.Serializer):
|
||||
email = serializers.EmailField(label="邮箱")
|
||||
|
||||
|
||||
class EmailCodeLoginSerializer(serializers.Serializer):
|
||||
email = serializers.EmailField(label="邮箱")
|
||||
code = serializers.CharField(label="验证码")
|
||||
|
||||
|
||||
class RegisterSerializer(serializers.Serializer):
|
||||
email = serializers.EmailField(label="邮箱")
|
||||
code = serializers.CharField(label="验证码", max_length=6)
|
||||
nickname = serializers.CharField(label="昵称", max_length=20)
|
||||
password = serializers.CharField(label="密码", min_length=6, max_length=50)
|
||||
enterprise_name = serializers.CharField(label="企业名称", max_length=200)
|
||||
credit_code = serializers.CharField(label="统一社会信用代码", max_length=18)
|
||||
|
|
@ -4,6 +4,8 @@ from rest_framework_simplejwt.views import TokenRefreshView
|
|||
|
||||
from apps.auth1.views import (CodeLogin, LoginView, LogoutView, PwResetView,
|
||||
SecretLogin, SendCode, TokenBlackView, WxLogin, WxmpLogin, TokenLoginView, FaceLoginView)
|
||||
from apps.auth1.register_view import RegisterView
|
||||
from apps.auth1.email_code_view import EmailCodeView
|
||||
|
||||
API_BASE_URL = 'api/auth/'
|
||||
urlpatterns = [
|
||||
|
|
@ -18,5 +20,7 @@ urlpatterns = [
|
|||
path(API_BASE_URL + 'sms_code/', SendCode.as_view(), name='sms_code_send'),
|
||||
path(API_BASE_URL + 'logout/', LogoutView.as_view(), name='session_logout'),
|
||||
path(API_BASE_URL + 'reset_password/', PwResetView.as_view(), name='reset_password'),
|
||||
path(API_BASE_URL + 'login_face/', FaceLoginView.as_view(), name='face_login')
|
||||
path(API_BASE_URL + 'login_face/', FaceLoginView.as_view(), name='face_login'),
|
||||
path(API_BASE_URL + 'email_code/', EmailCodeView.as_view(), name='email_code'),
|
||||
path(API_BASE_URL + 'register/', RegisterView.as_view(), name='register')
|
||||
]
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ from apps.auth1.serializers import FaceLoginSerializer
|
|||
|
||||
|
||||
from apps.auth1.serializers import (CodeLoginSerializer, LoginSerializer,
|
||||
PwResetSerializer, SecretLoginSerializer, SendCodeSerializer, WxCodeSerializer)
|
||||
PwResetSerializer, SecretLoginSerializer, SendCodeSerializer, WxCodeSerializer, RegisterSerializer, EmailCodeSerializer, EmailCodeLoginSerializer)
|
||||
from apps.system.models import User
|
||||
from rest_framework_simplejwt.views import TokenObtainPairView
|
||||
from apps.auth1.authentication import get_user_by_username_or
|
||||
|
|
@ -304,3 +304,64 @@ class FaceLoginView(CreateAPIView):
|
|||
else:
|
||||
raise ParseError('账户不存在或不可用')
|
||||
raise ParseError(msg)
|
||||
|
||||
|
||||
class EmailCodeView(CreateAPIView):
|
||||
"""发送邮箱验证码
|
||||
|
||||
发送邮箱验证码
|
||||
"""
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
serializer_class = EmailCodeSerializer
|
||||
|
||||
def post(self, request):
|
||||
"""邮箱验证码发送
|
||||
|
||||
邮箱验证码发送
|
||||
"""
|
||||
from apps.utils.tasks import send_mail_task
|
||||
email = request.data['email']
|
||||
code = rannum(6)
|
||||
|
||||
# 缓存验证码,有效期5分钟
|
||||
cache_key = f'email_code_{email}'
|
||||
cache.set(cache_key, code, 60 * 5)
|
||||
|
||||
# 使用 send_mail_task 发送验证码邮件
|
||||
send_mail_task.delay(
|
||||
subject='登录验证码',
|
||||
message=f'您的验证码是:{code},5分钟内有效。如非本人操作,请忽略此邮件。',
|
||||
recipient_list=[email]
|
||||
)
|
||||
|
||||
return Response({'message': '验证码已发送'})
|
||||
|
||||
|
||||
class EmailCodeLogin(CreateAPIView):
|
||||
"""邮箱验证码登录
|
||||
|
||||
邮箱验证码登录
|
||||
"""
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
serializer_class = EmailCodeLoginSerializer
|
||||
|
||||
def post(self, request):
|
||||
email = request.data['email']
|
||||
code = request.data['code']
|
||||
|
||||
# 验证邮箱验证码
|
||||
cache_key = f'email_code_{email}'
|
||||
cached_code = cache.get(cache_key)
|
||||
if not cached_code or cached_code != code:
|
||||
raise ParseError('验证码错误或已过期')
|
||||
|
||||
# 查找用户
|
||||
user = User.objects.filter(username=email).first()
|
||||
if user:
|
||||
ret = get_tokens_for_user(user)
|
||||
# 删除已使用的验证码
|
||||
cache.delete(cache_key)
|
||||
return Response(ret)
|
||||
raise ParseError('账户不存在或已禁用')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
# Generated by Django 4.2.27 on 2026-03-09 01:29
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('system', '0007_alter_dept_create_by_alter_dept_update_by_and_more'),
|
||||
('carbon', '0002_fingerprint'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Enterprise',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('name', models.CharField(max_length=200, verbose_name='企业名称')),
|
||||
('credit_code', models.CharField(max_length=18, unique=True, verbose_name='统一社会信用代码')),
|
||||
('legal_representative', models.CharField(blank=True, max_length=50, null=True, verbose_name='法定代表人')),
|
||||
('registered_capital', models.DecimalField(blank=True, decimal_places=2, max_digits=20, null=True, verbose_name='注册资本')),
|
||||
('establishment_date', models.DateField(blank=True, null=True, verbose_name='成立日期')),
|
||||
('business_scope', models.TextField(blank=True, null=True, verbose_name='经营范围')),
|
||||
('address', models.CharField(blank=True, max_length=500, null=True, verbose_name='注册地址')),
|
||||
('contact_phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='联系电话')),
|
||||
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='电子邮箱')),
|
||||
('industry', models.CharField(blank=True, max_length=100, null=True, verbose_name='所属行业')),
|
||||
('business_status', models.CharField(blank=True, max_length=50, null=True, verbose_name='经营状态')),
|
||||
('registration_authority', models.CharField(blank=True, max_length=200, null=True, verbose_name='登记机关')),
|
||||
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '企业信息',
|
||||
'verbose_name_plural': '企业信息',
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -52,3 +52,23 @@ class Fingerprint(BaseModel):
|
|||
models.Index(fields=["seg3"]),
|
||||
models.Index(fields=["seg4"]),
|
||||
]
|
||||
|
||||
|
||||
class Enterprise(CommonBDModel):
|
||||
"""企业信息表"""
|
||||
name = models.CharField("企业名称", max_length=200)
|
||||
credit_code = models.CharField("统一社会信用代码", max_length=18, unique=True)
|
||||
legal_representative = models.CharField("法定代表人", max_length=50, blank=True, null=True)
|
||||
registered_capital = models.DecimalField("注册资本", max_digits=20, decimal_places=2, blank=True, null=True)
|
||||
establishment_date = models.DateField("成立日期", blank=True, null=True)
|
||||
business_scope = models.TextField("经营范围", blank=True, null=True)
|
||||
address = models.CharField("注册地址", max_length=500, blank=True, null=True)
|
||||
contact_phone = models.CharField("联系电话", max_length=20, blank=True, null=True)
|
||||
email = models.EmailField("电子邮箱", blank=True, null=True)
|
||||
industry = models.CharField("所属行业", max_length=100, blank=True, null=True)
|
||||
business_status = models.CharField("经营状态", max_length=50, blank=True, null=True)
|
||||
registration_authority = models.CharField("登记机关", max_length=200, blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "企业信息"
|
||||
verbose_name_plural = verbose_name
|
||||
|
|
|
|||
Loading…
Reference in New Issue