移动端认证,短信发送等
This commit is contained in:
parent
dae9b5cfcc
commit
28f2daca2b
|
@ -1,3 +1,5 @@
|
|||
from django.db import models
|
||||
|
||||
from apps.utils.models import BaseModel
|
||||
|
||||
# Create your models here.
|
||||
|
|
|
@ -4,3 +4,12 @@ from rest_framework import serializers
|
|||
class LoginSerializer(serializers.Serializer):
|
||||
username = serializers.CharField(label="用户名")
|
||||
password = serializers.CharField(label="密码")
|
||||
|
||||
|
||||
class SendCodeSerializer(serializers.Serializer):
|
||||
phone = serializers.CharField(label="手机号")
|
||||
|
||||
|
||||
class CodeLoginSerializer(serializers.Serializer):
|
||||
phone = serializers.CharField(label="手机号")
|
||||
code = serializers.CharField(label="验证码")
|
||||
|
|
|
@ -3,7 +3,7 @@ from django.urls import path
|
|||
from rest_framework_simplejwt.views import (TokenObtainPairView,
|
||||
TokenRefreshView)
|
||||
|
||||
from apps.auth1.views import LoginView, LogoutView, TokenBlackView
|
||||
from apps.auth1.views import CodeLogin, GetTokenFromCache, LoginView, LogoutView, SendCode, TokenBlackView, WxLogin, WxmpLogin
|
||||
|
||||
API_BASE_URL = 'api/auth/'
|
||||
urlpatterns = [
|
||||
|
@ -11,5 +11,10 @@ urlpatterns = [
|
|||
path(API_BASE_URL + 'token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
|
||||
path(API_BASE_URL + 'token/black/', TokenBlackView.as_view(), name='token_black'),
|
||||
path(API_BASE_URL + 'login/', LoginView.as_view(), name='session_login'),
|
||||
path(API_BASE_URL + 'login_wxmp/', WxmpLogin.as_view(), name='login_wxmp'),
|
||||
path(API_BASE_URL + 'login_wx/', WxLogin.as_view(), name='login_wx'),
|
||||
path(API_BASE_URL + 'login_sms_code/', CodeLogin.as_view(), name='login_sms_code'),
|
||||
path(API_BASE_URL + 'sms_code/', SendCode.as_view(), name='sms_code_send'),
|
||||
path(API_BASE_URL + 'code_cache_token/', GetTokenFromCache.as_view(), name='code_cache_token'),
|
||||
path(API_BASE_URL + 'logout/', LogoutView.as_view(), name='session_logout'),
|
||||
]
|
||||
|
|
|
@ -8,15 +8,30 @@ from rest_framework.generics import CreateAPIView
|
|||
from rest_framework.permissions import IsAuthenticated
|
||||
from apps.auth1.errors import USERNAME_OR_PASSWORD_WRONG
|
||||
from rest_framework.exceptions import ParseError
|
||||
import requests
|
||||
import json
|
||||
from django.conf import settings
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from django.core.cache import cache
|
||||
from apps.utils.sms import send_sms
|
||||
from apps.utils.tools import rannum
|
||||
|
||||
|
||||
|
||||
from apps.auth1.serializers import LoginSerializer
|
||||
from apps.auth1.serializers import LoginSerializer, SendCodeSerializer
|
||||
from apps.system.models import User
|
||||
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
||||
|
||||
# Create your views here.
|
||||
|
||||
|
||||
def get_tokens_for_user(user: User):
|
||||
refresh = RefreshToken.for_user(user)
|
||||
return {
|
||||
'refresh': str(refresh),
|
||||
'access': str(refresh.access_token),
|
||||
}
|
||||
|
||||
|
||||
class TokenBlackView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
|
@ -66,3 +81,109 @@ class LogoutView(APIView):
|
|||
"""
|
||||
logout(request)
|
||||
return Response()
|
||||
|
||||
|
||||
class WxmpLogin(CreateAPIView):
|
||||
"""微信小程序自动登录
|
||||
|
||||
微信小程序自动登录
|
||||
"""
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
|
||||
def post(self, request):
|
||||
code = request.data['code']
|
||||
info = requests.get('https://api.weixin.qq.com/sns/jscode2session?appid='+settings.WXMP_APPID+'&secret='+settings.WXMP_APPSECRET+'&js_code=' +
|
||||
code+'&grant_type=authorization_code').content.decode('utf-8')
|
||||
info = json.loads(info)
|
||||
openid = info['openid']
|
||||
session_key = info['session_key']
|
||||
try:
|
||||
user = User.objects.get(wxmp_openid=openid, is_active=True)
|
||||
ret = get_tokens_for_user(user)
|
||||
ret['wxmp_session_key'] = session_key
|
||||
ret['wxmp_openid'] = openid
|
||||
cache.set(code, ret, 60*5)
|
||||
return Response(ret)
|
||||
except Exception:
|
||||
return Response({'wxmp_openid': openid, 'wxmp_session_key': session_key}, status=400)
|
||||
|
||||
|
||||
class WxLogin(CreateAPIView):
|
||||
"""微信公众号授权登录
|
||||
|
||||
微信公众号授权登录
|
||||
"""
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
|
||||
def post(self, request):
|
||||
code = request.data['code']
|
||||
info = requests.get('https://api.weixin.qq.com/sns/jscode2session?appid='+settings.WXMP_APPID+'&secret='+settings.WXMP_APPSECRET+'&js_code=' +
|
||||
code+'&grant_type=authorization_code').content.decode('utf-8')
|
||||
info = json.loads(info)
|
||||
openid = info['openid']
|
||||
session_key = info['session_key']
|
||||
try:
|
||||
user = User.objects.get(wx_openid=openid, is_active=True)
|
||||
ret = get_tokens_for_user(user)
|
||||
ret['wx_session_key'] = session_key
|
||||
ret['wx_openid'] = openid
|
||||
cache.set(code, ret, 60*5)
|
||||
return Response(ret)
|
||||
except Exception:
|
||||
return Response({'wx_openid': openid, 'wx_session_key': session_key}, status=400)
|
||||
|
||||
|
||||
class GetTokenFromCache(CreateAPIView):
|
||||
"""以code获取token
|
||||
|
||||
以code获取token
|
||||
"""
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
|
||||
def post(self, request):
|
||||
code = request.data['code']
|
||||
ret = cache.get(code, {})
|
||||
return Response(ret)
|
||||
|
||||
|
||||
class SendCode(CreateAPIView):
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
serializer_class = SendCodeSerializer
|
||||
|
||||
def post(self, request):
|
||||
"""短信验证码发送
|
||||
|
||||
短信验证码发送
|
||||
"""
|
||||
phone = request.data['phone']
|
||||
code = rannum(4)
|
||||
is_ok, _ = send_sms(phone, 505, {'code': code})
|
||||
cache.set(phone, code, 60*5)
|
||||
if is_ok:
|
||||
return Response()
|
||||
raise ParseError('短信发送失败,请确认手机号')
|
||||
|
||||
|
||||
class CodeLogin(CreateAPIView):
|
||||
"""手机验证码登录
|
||||
|
||||
手机验证码登录
|
||||
"""
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
|
||||
def post(self, request):
|
||||
phone = request.data['phone']
|
||||
code = request.data['code']
|
||||
code_exist = cache.get(phone, None)
|
||||
if code_exist == code:
|
||||
user = User.objects.filter(phone=phone).first()
|
||||
if user:
|
||||
ret = get_tokens_for_user(user)
|
||||
return Response(ret)
|
||||
raise ParseError('账户不存在或已禁用')
|
||||
raise ParseError('验证码错误')
|
||||
|
|
|
@ -90,8 +90,8 @@ def notify_event(event: Event):
|
|||
event (Event): _description_
|
||||
"""
|
||||
# 生成通知文本
|
||||
voice_msg = ''
|
||||
ep = event.employee
|
||||
params = {'area': event.area.name, 'employee': '', 'event': ''}
|
||||
if ep:
|
||||
ep_name = ep.name
|
||||
ep_type = '员工'
|
||||
|
@ -99,17 +99,17 @@ def notify_event(event: Event):
|
|||
ep_type = '相关方人员'
|
||||
elif ep.type == 'visitor':
|
||||
ep_type = '访客'
|
||||
voice_msg = '位于{}的{}{},'.format(event.area.name, ep_type, ep_name)
|
||||
params['employee'] = ep_type + ep_name
|
||||
else:
|
||||
voice_msg = '位于{}的未知人员,'.format(event.area.name)
|
||||
params['employee'] = '未知人员'
|
||||
for i in event.cates.all():
|
||||
voice_msg = voice_msg + i.name + ','
|
||||
event.voice_msg = voice_msg + '请及时处理'
|
||||
params['event'] = params['event'] + i.name + ','
|
||||
event.voice_msg = '位于{}的{},{}请及时处理'.format(params['area'], params['employee'], params['event'])
|
||||
event.save()
|
||||
# 喇叭播放
|
||||
Thread(target=save_voice_and_speak, args=(event,), daemon=True).start()
|
||||
# 创建提醒
|
||||
Thread(target=create_remind, args=(event,), daemon=True).start()
|
||||
Thread(target=create_remind, args=(event, params), daemon=True).start()
|
||||
|
||||
|
||||
def save_voice_and_speak(event: Event):
|
||||
|
@ -135,7 +135,7 @@ def save_voice_and_speak(event: Event):
|
|||
spClient.speak(event.voice, sps, v_num)
|
||||
|
||||
|
||||
def create_remind(event: Event):
|
||||
def create_remind(event: Event, params: dict):
|
||||
"""
|
||||
创建事件提醒并发送短信/微信
|
||||
"""
|
||||
|
@ -203,10 +203,11 @@ def create_remind(event: Event):
|
|||
})
|
||||
# 开始发送通知
|
||||
for i in Remind.objects.filter(event=event):
|
||||
if i.notify_setting.sms_enable:
|
||||
if i.recipient.employee.phone: # 短信通知
|
||||
Thread(target=send_sms, args=(i.recipient.employee.phone,
|
||||
'1001', {'code': '5678'}), daemon=True).start()
|
||||
if i.notify_setting.sms_enable and i.recipient.employee.phone:
|
||||
# 发送短信通知
|
||||
|
||||
Thread(target=send_sms, args=(i.recipient.employee.phone,
|
||||
'1003', params), daemon=True).start()
|
||||
if i.notify_setting.wechat_enable:
|
||||
pass
|
||||
event.is_pushed = True
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# Generated by Django 3.2.12 on 2022-07-24 05:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('system', '0003_alter_user_phone'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='wx_headimg',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='微信头像'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='wx_nickname',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='微信昵称'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='wx_openid',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='微信公众号OpenId'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='wxmp_openid',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='微信小程序OpenId'),
|
||||
),
|
||||
]
|
|
@ -134,6 +134,12 @@ class User(AbstractUser, CommonBModel):
|
|||
depts = models.ManyToManyField(Dept, through='system.userpost')
|
||||
roles = models.ManyToManyField(Role, verbose_name='关联角色')
|
||||
|
||||
# 关联账号
|
||||
wx_openid = models.CharField('微信公众号OpenId', max_length=100, null=True, blank=True)
|
||||
wx_nickname = models.CharField('微信昵称', max_length=100, null=True, blank=True)
|
||||
wx_headimg = models.CharField('微信头像', max_length=100, null=True, blank=True)
|
||||
wxmp_openid = models.CharField('微信小程序OpenId', max_length=100, null=True, blank=True)
|
||||
|
||||
objects = SoftDeletableUserManager()
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -462,10 +462,53 @@ class UserViewSet(CustomModelViewSet):
|
|||
'perms': perms,
|
||||
'belong_dept_name': user.belong_dept.name if user.belong_dept else '',
|
||||
'post_name': user.post.name if user.post else '',
|
||||
'is_superuser': user.is_superuser
|
||||
'is_superuser': user.is_superuser,
|
||||
'wxmp_openid': user.wxmp_openid,
|
||||
'wx_openid': user.wx_openid
|
||||
}
|
||||
return Response(data)
|
||||
|
||||
@action(methods=['post'], detail=False, permission_classes=[IsAuthenticated])
|
||||
def bind_wxmp(self, request, pk=None):
|
||||
"""
|
||||
绑定微信小程序
|
||||
|
||||
绑定微信小程序
|
||||
"""
|
||||
openid = request.data['openid']
|
||||
user = request.user
|
||||
if user.wxmp_openid != openid:
|
||||
User.objects.filter(wxmp_openid=openid).update(wxmp_openid=None)
|
||||
user.wxmp_openid = openid
|
||||
user.save()
|
||||
return Response({'wxmp_openid': openid})
|
||||
|
||||
@action(methods=['post'], detail=False, permission_classes=[IsAuthenticated])
|
||||
def unbind_wxmp(self, request, pk=None):
|
||||
"""
|
||||
解绑微信小程序
|
||||
|
||||
解绑微信小程序
|
||||
"""
|
||||
user = request.user
|
||||
user.wxmp_openid = None
|
||||
user.save()
|
||||
return Response()
|
||||
|
||||
@action(methods=['post'], detail=False, permission_classes=[IsAuthenticated])
|
||||
def bind_wx(self, request, pk=None):
|
||||
"""绑定微信公众号
|
||||
|
||||
绑定微信公众号, 用于发送通知
|
||||
"""
|
||||
openid = request.data['openid']
|
||||
user = request.user
|
||||
if user.wx_openid != openid:
|
||||
User.objects.filter(wx_openid=openid).update(wx_openid=None)
|
||||
user.wx_openid = openid
|
||||
user.save()
|
||||
return Response({'wx_openid': openid})
|
||||
|
||||
|
||||
class FileViewSet(CustomCreateModelMixin, RetrieveModelMixin, ListModelMixin, CustomGenericViewSet):
|
||||
"""文件上传
|
||||
|
|
|
@ -26,7 +26,7 @@ def send_sms(phone: str, template_code: str, template_param: dict):
|
|||
# 手机号码
|
||||
request.add_query_param('PhoneNumbers', phone)
|
||||
# 签名名称
|
||||
request.add_query_param('SignName', "国家建材网")
|
||||
request.add_query_param('SignName', "曲阳金隅EHS")
|
||||
# 模板CODE
|
||||
request.add_query_param('TemplateCode', template_code)
|
||||
# 如果有模板参数 填写模板参数 如果无 无须填写
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from requests import request
|
||||
from apps.hrm.models import Employee
|
||||
from apps.utils.constants import EXCLUDE_FIELDS
|
||||
from apps.utils.serializers import CustomModelSerializer
|
||||
|
@ -80,7 +81,9 @@ class VpeopleSerializer(CustomModelSerializer):
|
|||
|
||||
class VisitorRegisterSerializer(serializers.Serializer):
|
||||
name = serializers.CharField(label="姓名")
|
||||
username = serializers.CharField(label='用户名', min_length=6)
|
||||
username = serializers.CharField(label="账户名", required=False)
|
||||
wxmp_openid = serializers.CharField(label='微信小程序ID', required=False)
|
||||
wx_openid = serializers.CharField(label='微信公众号ID', required=False)
|
||||
|
||||
|
||||
class VisitDetailSerializer(CustomModelSerializer):
|
||||
|
|
|
@ -75,16 +75,21 @@ class VisitorViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Custo
|
|||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
vdata = serializer.validated_data
|
||||
user = User.objects.get_queryset(all=True).filter(username=vdata['username']).first()
|
||||
if user:
|
||||
raise ParseError('账号已存在或禁用不可注册')
|
||||
else:
|
||||
password = make_password('0000')
|
||||
user = User.objects.create(name=vdata['name'],
|
||||
username=vdata['username'],
|
||||
type='visitor',
|
||||
password=password)
|
||||
return Response({'user': user.id}, status=201)
|
||||
if vdata.get('username', None): # 如果是用户名注册
|
||||
user = User.objects.get_queryset(all=True).filter(username=vdata['username']).first()
|
||||
if user:
|
||||
raise ParseError('账号已存在或禁用不可注册')
|
||||
else:
|
||||
password = make_password('0000')
|
||||
user = User.objects.create(name=vdata['name'],
|
||||
username=vdata['username'],
|
||||
type='visitor',
|
||||
password=password)
|
||||
return Response({'user': user.id}, status=201)
|
||||
elif vdata.get('wxmp_openid', None): # 如果是微信小程序注册
|
||||
pass
|
||||
elif vdata.get('wx_openid', None): # 如果是微信公众号注册
|
||||
pass
|
||||
|
||||
|
||||
class VpeopleViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, DestroyModelMixin, CustomGenericViewSet):
|
||||
|
|
|
@ -3,18 +3,33 @@ from __future__ import absolute_import, unicode_literals
|
|||
import importlib
|
||||
import logging
|
||||
import traceback
|
||||
from apps.system.models import User
|
||||
from apps.utils.sms import send_sms
|
||||
from apps.utils.task import CustomTask
|
||||
from celery import shared_task
|
||||
from apps.wf.models import State, Ticket, TicketFlow, Transition
|
||||
|
||||
myLogger = logging.getLogger('log')
|
||||
|
||||
|
||||
@shared_task(base=CustomTask)
|
||||
def send_ticket_notice(ticket_id):
|
||||
"""
|
||||
发送通知
|
||||
"""
|
||||
pass
|
||||
ticket = Ticket.objects.filter(id=ticket_id).first()
|
||||
params = {'workflow': ticket.workflow.name, 'state': ticket.state.name}
|
||||
if ticket:
|
||||
if ticket.participant_type == 1:
|
||||
# 发送短信通知
|
||||
pt = User.objects.filter(id=ticket.participant).first()
|
||||
if pt and pt.phone:
|
||||
send_sms(pt.phone, 1002, params)
|
||||
elif ticket.participant_type == 2:
|
||||
pts = User.objects.filter(id__in=ticket.participant).first()
|
||||
for i in pts:
|
||||
if i.phone:
|
||||
send_sms(i.phone, 1002, params)
|
||||
|
||||
|
||||
@shared_task(base=CustomTask)
|
||||
|
|
|
@ -394,3 +394,8 @@ BD_SP_SECRET = conf.BD_SP_SECRET
|
|||
|
||||
# 算法层
|
||||
AI_IP = conf.AI_IP
|
||||
|
||||
|
||||
# 微信有关
|
||||
WXMP_APPID = conf.WXMP_APPID
|
||||
WXMP_APPSECRET = conf.WXMP_APPSECRET
|
||||
|
|
Loading…
Reference in New Issue