From 0bac3e55f4951f0f760bbca7bdb98b8e3c7bcf48 Mon Sep 17 00:00:00 2001 From: youngS Date: Sun, 26 Sep 2021 17:34:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BE=AE=E4=BF=A1=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E6=8E=A8=E9=80=81=E6=A8=A1=E6=9D=BF=EF=BC=8C=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=90=8E=E5=8F=B0=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E7=BB=91=E5=AE=9A=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fir_admin/src/api/wxbind.js | 25 +++ fir_admin/src/router/index.js | 19 ++ fir_admin/src/views/domain/list.vue | 2 +- fir_admin/src/views/wxbind/Detail.vue | 152 ++++++++++++++ fir_admin/src/views/wxbind/list.vue | 201 ++++++++++++++++++ fir_ser/admin/urls.py | 3 +- fir_ser/admin/views/login.py | 3 - fir_ser/admin/views/user.py | 60 +++++- fir_ser/api/utils/auth.py | 12 ++ fir_ser/api/utils/baseutils.py | 7 + fir_ser/api/utils/geetest/geetest_utils.py | 14 +- fir_ser/api/utils/mp/wechat.py | 228 ++++++++++++++++++++- fir_ser/api/utils/serializer.py | 9 +- fir_ser/api/views/domain.py | 3 +- fir_ser/api/views/download.py | 7 +- fir_ser/api/views/login.py | 16 +- fir_ser/api/views/thirdlogin.py | 96 +++++++-- 17 files changed, 814 insertions(+), 43 deletions(-) create mode 100644 fir_admin/src/api/wxbind.js create mode 100644 fir_admin/src/views/wxbind/Detail.vue create mode 100644 fir_admin/src/views/wxbind/list.vue diff --git a/fir_admin/src/api/wxbind.js b/fir_admin/src/api/wxbind.js new file mode 100644 index 0000000..456c962 --- /dev/null +++ b/fir_admin/src/api/wxbind.js @@ -0,0 +1,25 @@ +import request from '@/utils/request' + +export function getWxBindInfos(query) { + return request({ + url: '/wxbind/info', + method: 'get', + params: query + }) +} + +export function updateWxBindInfo(data) { + return request({ + url: '/wxbind/info', + method: 'put', + data + }) +} + +export function deleteWxBind(data) { + return request({ + url: '/wxbind/info', + method: 'delete', + data + }) +} diff --git a/fir_admin/src/router/index.js b/fir_admin/src/router/index.js index 22ae2c6..89acf09 100644 --- a/fir_admin/src/router/index.js +++ b/fir_admin/src/router/index.js @@ -194,6 +194,25 @@ export const constantRoutes = [ } ] }, + { + path: '/wxbind', + component: Layout, + children: [ + { + path: 'list', + name: 'wxbind_info_list', + component: () => import('@/views/wxbind/list'), + meta: { title: '微信绑定管理', icon: 'form' } + }, + { + path: 'edit/:id(\\d+)', + component: () => import('@/views/wxbind/Detail'), + name: 'wxbind_info_edit', + meta: { title: '编辑信息', noCache: true, activeMenu: '/wxbind/list' }, + hidden: true + } + ] + }, { path: '/supersign', component: Layout, diff --git a/fir_admin/src/views/domain/list.vue b/fir_admin/src/views/domain/list.vue index 961763d..6948fcb 100644 --- a/fir_admin/src/views/domain/list.vue +++ b/fir_admin/src/views/domain/list.vue @@ -38,7 +38,7 @@ - + diff --git a/fir_admin/src/views/wxbind/Detail.vue b/fir_admin/src/views/wxbind/Detail.vue new file mode 100644 index 0000000..adbf345 --- /dev/null +++ b/fir_admin/src/views/wxbind/Detail.vue @@ -0,0 +1,152 @@ + + + + + diff --git a/fir_admin/src/views/wxbind/list.vue b/fir_admin/src/views/wxbind/list.vue new file mode 100644 index 0000000..1916798 --- /dev/null +++ b/fir_admin/src/views/wxbind/list.vue @@ -0,0 +1,201 @@ + + + diff --git a/fir_ser/admin/urls.py b/fir_ser/admin/urls.py index dfde0ca..c161bf3 100644 --- a/fir_ser/admin/urls.py +++ b/fir_ser/admin/urls.py @@ -15,7 +15,7 @@ Including another URLconf """ from django.urls import re_path from admin.views.login import LoginView, LoginUserView -from admin.views.user import UserInfoView, UserCertificationInfoView +from admin.views.user import UserInfoView, UserCertificationInfoView, ThirdWxAccountView from admin.views.app import AppInfoView, AppReleaseInfoView from admin.views.storage import StorageInfoView, StorageChangeView from admin.views.order import OrderInfoView @@ -38,5 +38,6 @@ urlpatterns = [ re_path("^developer/info", DeveloperInfoView.as_view()), re_path("^devices/info", DevicesInfoView.as_view()), re_path("^domain/info", DomainNameInfoView.as_view()), + re_path("^wxbind/info", ThirdWxAccountView.as_view()), ] diff --git a/fir_ser/admin/views/login.py b/fir_ser/admin/views/login.py index 0568c32..242354e 100644 --- a/fir_ser/admin/views/login.py +++ b/fir_ser/admin/views/login.py @@ -5,13 +5,10 @@ # date: 2021/4/11 from django.contrib import auth -from api.models import Token, UserInfo from rest_framework.response import Response from api.utils.auth import ExpiringTokenAuthentication from api.utils.serializer import UserInfoSerializer -from django.core.cache import cache from rest_framework.views import APIView -import os, datetime from api.utils.utils import get_captcha, valid_captcha, set_user_token from api.utils.response import BaseResponse from fir_ser.settings import CACHE_KEY_TEMPLATE, LOGIN diff --git a/fir_ser/admin/views/user.py b/fir_ser/admin/views/user.py index fc2aba2..2b5513e 100644 --- a/fir_ser/admin/views/user.py +++ b/fir_ser/admin/views/user.py @@ -5,11 +5,11 @@ # date: 2021/4/11 from django.contrib import auth -from api.models import Token, UserInfo, UserCertificationInfo +from api.models import Token, UserInfo, UserCertificationInfo, ThirdWeChatUserInfo from rest_framework.response import Response from api.utils.auth import AdminTokenAuthentication from api.utils.baseutils import get_dict_from_filter_fields -from api.utils.serializer import AdminUserInfoSerializer, AdminUserCertificationSerializer +from api.utils.serializer import AdminUserInfoSerializer, AdminUserCertificationSerializer, AdminThirdWxSerializer from django.core.cache import cache from rest_framework.views import APIView import binascii @@ -25,7 +25,7 @@ from rest_framework.pagination import PageNumberPagination logger = logging.getLogger(__name__) -class AppsPageNumber(PageNumberPagination): +class PageNumber(PageNumberPagination): page_size = 20 # 每页显示多少条 page_size_query_param = 'limit' # URL中每页显示条数的参数 page_query_param = 'page' # URL中页码的参数 @@ -46,7 +46,7 @@ class UserInfoView(APIView): filter_data["certification__status__isnull"] = True else: filter_data["certification__status"] = certification - page_obj = AppsPageNumber() + page_obj = PageNumber() obj_list = UserInfo.objects.filter(**filter_data).order_by(sort) page_serializer = page_obj.paginate_queryset(queryset=obj_list, request=request, view=self) @@ -87,7 +87,7 @@ class UserCertificationInfoView(APIView): filter_fields = ["id", "card", "name", "status"] filter_data = get_dict_from_filter_fields(filter_fields, request.query_params) sort = request.query_params.get("sort", "-created_time") - page_obj = AppsPageNumber() + page_obj = PageNumber() obj_list = UserCertificationInfo.objects.filter(**filter_data).order_by(sort) page_serializer = page_obj.paginate_queryset(queryset=obj_list, request=request, view=self) @@ -115,3 +115,53 @@ class UserCertificationInfoView(APIView): res.code = 1004 res.msg = "数据校验失败" return Response(res.dict) + + +class ThirdWxAccountView(APIView): + authentication_classes = [AdminTokenAuthentication, ] + + def get(self, request): + res = BaseResponse() + filter_fields = ["id", "openid", "nickname", "subscribe", "user_id"] + filter_data = get_dict_from_filter_fields(filter_fields, request.query_params) + sort = request.query_params.get("sort", "-created_time") + page_obj = PageNumber() + obj_list = ThirdWeChatUserInfo.objects.filter(**filter_data).order_by(sort) + page_serializer = page_obj.paginate_queryset(queryset=obj_list, request=request, + view=self) + serializer = AdminThirdWxSerializer(page_serializer, many=True) + res.data = serializer.data + res.total = obj_list.count() + return Response(res.dict) + + def put(self, request): + res = BaseResponse() + data = request.data + pk = data.get("id", None) + if not pk: + res.code = 1003 + res.msg = "参数错误" + return Response(res.dict) + obj = ThirdWeChatUserInfo.objects.filter(pk=pk).first() + if obj: + data['pk'] = pk + serializer_obj = AdminThirdWxSerializer(obj, data=data, partial=True) + if serializer_obj.is_valid(): + serializer_obj.save() + res.data = serializer_obj.data + return Response(res.dict) + res.code = 1004 + res.msg = "数据校验失败" + return Response(res.dict) + + def delete(self, request): + res = BaseResponse() + data = request.data + pk = data.get("id", None) + if not pk: + res.code = 1003 + res.msg = "参数错误" + else: + ThirdWeChatUserInfo.objects.filter(pk=pk).delete() + return self.get(request) + return Response(res.dict) diff --git a/fir_ser/api/utils/auth.py b/fir_ser/api/utils/auth.py index 04da9d9..12001ca 100644 --- a/fir_ser/api/utils/auth.py +++ b/fir_ser/api/utils/auth.py @@ -105,3 +105,15 @@ class SuperSignPermission(BasePermission): if results and results[0]: return results[0].supersign_active return True + + +class DownloadQrPermission(BasePermission): + message = "权限不足" + + def has_permission(self, request, view): + domain_type = request.query_params.get('domain_type', -1) + results = get_user_from_request_auth(request) + if results and results[0]: + if domain_type == '0' and results[0].role < 2: + return False + return True diff --git a/fir_ser/api/utils/baseutils.py b/fir_ser/api/utils/baseutils.py index 341583b..0741a8f 100644 --- a/fir_ser/api/utils/baseutils.py +++ b/fir_ser/api/utils/baseutils.py @@ -246,3 +246,10 @@ def check_app_domain_name_access(app_obj, access_domain_name, extra_domain=None) if user_domain_name: domain_list.append(user_domain_name) if access_domain_name in domain_list: return True + + +def get_real_ip_address(request): + if request.META.get('HTTP_X_FORWARDED_FOR', None): + return request.META.get('HTTP_X_FORWARDED_FOR') + else: + return request.META.get('REMOTE_ADDR') diff --git a/fir_ser/api/utils/geetest/geetest_utils.py b/fir_ser/api/utils/geetest/geetest_utils.py index 0bf13d5..3c43f70 100644 --- a/fir_ser/api/utils/geetest/geetest_utils.py +++ b/fir_ser/api/utils/geetest/geetest_utils.py @@ -35,11 +35,17 @@ def check_bypass_status(): # 从缓存中取出当前缓存的bypass状态(success/fail) -def get_bypass_cache(): +def get_bypass_cache(count=3): bypass_status_cache = redis_connect.get(GEETEST_BYPASS_STATUS_KEY) - if not bypass_status_cache: - bypass_status_cache = 'fail' - return bypass_status_cache + if bypass_status_cache: + return bypass_status_cache + else: + if count > 0: + check_bypass_status() + count -= 1 + return get_bypass_cache(count) + else: + return 'fail' # 验证初始化接口,GET请求 diff --git a/fir_ser/api/utils/mp/wechat.py b/fir_ser/api/utils/mp/wechat.py index 4c9be20..a58b038 100644 --- a/fir_ser/api/utils/mp/wechat.py +++ b/fir_ser/api/utils/mp/wechat.py @@ -10,6 +10,7 @@ import json from django.core.cache import cache +from api.utils.baseutils import get_format_time from fir_ser.settings import THIRDLOGINCONF, CACHE_KEY_TEMPLATE from api.utils.mp.utils import WxMsgCryptBase @@ -65,15 +66,25 @@ def create_menu(): "name": "分发平台", "sub_button": [ { - "type": "view", + "type": "click", "name": "官方地址", - "url": "https://flyapps.cn" + "key": "flyapps" }, { "type": "view", "name": "留言反馈", "url": "https://flyapps.cn/gbook/" }, + { + "type": "click", + "name": "查询登录绑定", + "key": "query_bind" + }, + { + "type": "click", + "name": "解除登录绑定", + "key": "unbind" + }, ] }, { @@ -156,3 +167,216 @@ def check_signature(params): class WxMsgCrypt(WxMsgCryptBase): def __init__(self): super().__init__(**wx_login_info.get('auth')) + + +class WxTemplateMsg(object): + + def send_msg(self, to_user, template_id, content): + msg_uri = f'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={get_wx_access_token_cache()}' + data = { + "touser": to_user, + "template_id": template_id, + # "url": "http://weixin.qq.com/download", + "topcolor": "#FF0000", + "data": content + } + req = requests.post(msg_uri, json=data) + if req.status_code == 200: + return True, format_req_json(req.json(), get_userinfo_from_openid, to_user) + logger.error(f"send msg from openid failed {req.status_code} {req.text}") + return False, req.text + + def login_success_msg(self, to_user, wx_nick_name, username): + """ + 您进行了微信扫一扫登录操作 + 系统帐号:yin.xiaogang + 登录系统:OA系统 + 登录时间:2014-11-28 10:06:32 + 如有疑问,请致电IT服务台400-888-8888 或 关注公众号在线反馈 + :param to_user: + :param wx_nick_name: 微信昵称 + :param username: flyapps 用户昵称 + :return: + """ + now_time = get_format_time() + msg_id = 'EJhBbxJvHdWnwwexaqb0lCC2sM7D7WMex5-yJvTL5sU' + content_data = { + "first": { + "value": f"您的微信账户“{wx_nick_name}”进行了网站登录操作", + "color": "#173177" + }, + "keyword1": { + "value": username, + "color": "#173177" + }, + "keyword2": { + "value": "fly应用分发平台", + "color": "#173177" + }, + "keyword3": { + "value": now_time.replace('_', ' '), + "color": "#173177" + }, + "remark": { + "value": "感谢您的关注", + "color": "#173177" + }, + } + return self.send_msg(to_user, msg_id, content_data) + + def login_failed_msg(self, to_user, wx_nick_name): + now_time = get_format_time() + msg_id = '9rChJFw6nR0Wbp7SXsImh99qm6Dj1hrRWJo1NpEJ_3g' + content_data = { + "first": { + "value": f"您的微信账户“{wx_nick_name}”登录失败", + "color": "#173177" + }, + "keyword1": { + "value": "还未绑定用户,请通过手机或者邮箱登录账户之后进行绑定", + "color": "#173177" + }, + "keyword2": { + "value": now_time.replace('_', ' '), + "color": "#173177" + }, + "remark": { + "value": "感谢您的关注", + "color": "#173177" + }, + } + return self.send_msg(to_user, msg_id, content_data) + + def bind_success_msg(self, to_user, wx_nick_name, username): + now_time = get_format_time() + msg_id = 'twMMQn9AKZKevbZBYh8EcFMk7BnC5Y09FmDkZQEH43w' + content_data = { + "first": { + "value": f"您的微信账户“{wx_nick_name}”绑定成功", + "color": "#173177" + }, + "keyword1": { + "value": username, + "color": "#173177" + }, + "keyword2": { + "value": now_time.replace('_', ' '), + "color": "#173177" + }, + "remark": { + "value": "感谢您的关注", + "color": "#173177" + }, + } + return self.send_msg(to_user, msg_id, content_data) + + def bind_failed_msg(self, to_user, wx_nick_name, msg): + now_time = get_format_time() + msg_id = 'WIrRuHiDG0f976seBAmY-rjSil0AiT9E5l0PHrPnsfs' + content_data = { + "first": { + "value": f"您的微信账户“{wx_nick_name}”绑定失败", + "color": "#173177" + }, + "keyword1": { + "value": msg, + "color": "#173177" + }, + "keyword2": { + "value": now_time.replace('_', ' '), + "color": "#173177" + }, + "remark": { + "value": "每个微信只可以绑定一个登录账户,请先解绑,然后重新扫描绑定。感谢您的关注", + "color": "#173177" + }, + } + return self.send_msg(to_user, msg_id, content_data) + + def unbind_success_msg(self, to_user, wx_nick_name, username): + now_time = get_format_time() + msg_id = 'RabYMg8-jPGhonk957asbW17iLHSLp8BfEXnyesRZ60' + content_data = { + "first": { + "value": f"您的微信账户“{wx_nick_name}”已经解除绑定", + "color": "#173177" + }, + "keyword1": { + "value": username, + "color": "#173177" + }, + "keyword2": { + "value": now_time.replace('_', ' '), + "color": "#173177" + }, + "keyword3": { + "value": "解除绑定成功,您将无法使用微信扫描登录平台", + "color": "#173177" + }, + "remark": { + "value": "如需重新绑定,请登陆平台,在个人资料进行绑定。感谢您的关注", + "color": "#173177" + }, + } + return self.send_msg(to_user, msg_id, content_data) + + def bind_query_success_msg(self, to_user, wx_nick_name, username, name, mobile, email): + msg_id = 'yU15jLNSULagJTff01X67mDtDytBSs3iBpOBi8c7dvs' + content_data = { + "first": { + "value": f"您的微信账户“{wx_nick_name}”绑定信息结果", + "color": "#173177" + }, + "keyword1": { + "value": username, + "color": "#173177" + }, + "keyword2": { + "value": name, + "color": "#173177" + }, + "keyword3": { + "value": mobile, + "color": "#173177" + }, + "keyword4": { + "value": email, + "color": "#173177" + }, + "remark": { + "value": "感谢您的关注", + "color": "#173177" + }, + } + return self.send_msg(to_user, msg_id, content_data) + + def query_bind_info_failed_msg(self, to_user, wx_nick_name, action_msg, failed_msg): + now_time = get_format_time() + msg_id = 'uCxjYt216zRAv_sPZihKk4xp7-6pLmRW1oNLLW7L3oI' + content_data = { + "first": { + "value": f"您的微信账户“{wx_nick_name}” {action_msg}失败了", + "color": "#173177" + }, + "keyword1": { + "value": wx_nick_name, + "color": "#173177" + }, + "keyword2": { + "value": action_msg, + "color": "#173177" + }, + "keyword3": { + "value": failed_msg, + "color": "#173177" + }, + "keyword4": { + "value": now_time.replace('_', ' '), + "color": "#173177" + }, + "remark": { + "value": "暂无登录绑定信息,如需绑定,请登陆平台,在个人资料进行绑定。感谢您的关注", + "color": "#173177" + }, + } + return self.send_msg(to_user, msg_id, content_data) diff --git a/fir_ser/api/utils/serializer.py b/fir_ser/api/utils/serializer.py index cb61586..5330a21 100644 --- a/fir_ser/api/utils/serializer.py +++ b/fir_ser/api/utils/serializer.py @@ -614,7 +614,14 @@ class AdminUserCertificationSerializer(serializers.ModelSerializer): class ThirdWxSerializer(serializers.ModelSerializer): class Meta: model = models.ThirdWeChatUserInfo - exclude = ["id"] + exclude = ["id", "sex", "address", "user_id"] + + +class AdminThirdWxSerializer(ThirdWxSerializer): + class Meta: + model = models.ThirdWeChatUserInfo + fields = "__all__" + read_only_fields = ["id", "openid", "user_id", "head_img_url"] class DomainNameSerializer(serializers.ModelSerializer): diff --git a/fir_ser/api/views/domain.py b/fir_ser/api/views/domain.py index 32eeb8c..28e2688 100644 --- a/fir_ser/api/views/domain.py +++ b/fir_ser/api/views/domain.py @@ -9,7 +9,7 @@ from rest_framework.views import APIView from api.utils.baseutils import is_valid_domain, get_cname_from_domain, get_user_domain_name, \ get_min_default_domain_cname_obj from api.utils.response import BaseResponse -from api.utils.auth import ExpiringTokenAuthentication +from api.utils.auth import ExpiringTokenAuthentication, DownloadQrPermission from rest_framework.response import Response from api.models import UserDomainInfo, Apps import logging @@ -62,6 +62,7 @@ def add_new_domain_info(res, request, domain_name, domain_type): class DomainCnameView(APIView): authentication_classes = [ExpiringTokenAuthentication, ] + permission_classes = [DownloadQrPermission, ] def get(self, request): res = BaseResponse() diff --git a/fir_ser/api/views/download.py b/fir_ser/api/views/download.py index 765ca3d..e4eadb7 100644 --- a/fir_ser/api/views/download.py +++ b/fir_ser/api/views/download.py @@ -24,7 +24,7 @@ from api.models import Apps, AppReleaseInfo, APPToDeveloper, APPSuperSignUsedInf from django.http import FileResponse import logging from api.utils.baseutils import get_profile_full_path, get_app_domain_name, get_filename_form_file, \ - check_app_domain_name_access + check_app_domain_name_access, get_real_ip_address from api.utils.throttle import VisitShortThrottle, InstallShortThrottle, InstallThrottle1, InstallThrottle2 logger = logging.getLogger(__name__) @@ -228,10 +228,7 @@ class InstallView(APIView): res.data = {"download_url": download_url, "extra_url": extra_url} if download_url != "" and "mobileconifg" not in download_url: - if request.META.get('HTTP_X_FORWARDED_FOR', None): - ip = request.META['HTTP_X_FORWARDED_FOR'] - else: - ip = request.META['REMOTE_ADDR'] + ip = get_real_ip_address(request) logger.info(f"remote ip {ip} short {short} download_url {download_url} app_obj {app_obj}") set_app_download_by_cache(app_id) amount = app_obj.get("d_count") diff --git a/fir_ser/api/views/login.py b/fir_ser/api/views/login.py index 3642465..f384b46 100644 --- a/fir_ser/api/views/login.py +++ b/fir_ser/api/views/login.py @@ -10,7 +10,7 @@ from rest_framework.views import APIView from api.utils.utils import get_captcha, valid_captcha, \ get_sender_sms_token, is_valid_sender_code, get_sender_email_token, get_random_username, \ check_username_exists, set_user_token -from api.utils.baseutils import is_valid_phone, is_valid_email, get_min_default_domain_cname_obj +from api.utils.baseutils import is_valid_phone, is_valid_email, get_min_default_domain_cname_obj, get_real_ip_address from api.utils.auth import ExpiringTokenAuthentication from api.utils.response import BaseResponse from fir_ser.settings import REGISTER, LOGIN, CHANGER @@ -400,7 +400,7 @@ class RegistView(APIView): return Response(response.dict) -def wx_qr_code_response(ret, code, qr_info): +def wx_qr_code_response(ret, code, qr_info, ip_addr): if code: logger.info(f"微信登录码获取成功, {qr_info}") errmsg = qr_info.get('errmsg') @@ -410,7 +410,7 @@ def wx_qr_code_response(ret, code, qr_info): else: ticket = qr_info.get('ticket') if ticket: - set_wx_ticket_login_info_cache(ticket) + set_wx_ticket_login_info_cache(ticket, {'ip_addr': ip_addr}) ret.data = {'qr': show_qrcode_url(ticket), 'ticket': ticket} else: ret.code = 1003 @@ -533,7 +533,7 @@ class UserInfoView(APIView): ret = BaseResponse() uid = request.user.uid code, qr_info = make_wx_login_qrcode(f"web.bind.{uid}") - return wx_qr_code_response(ret, code, qr_info) + return wx_qr_code_response(ret, code, qr_info, get_real_ip_address(request)) class AuthorizationView(APIView): @@ -746,7 +746,7 @@ class WeChatLoginView(APIView): ret = BaseResponse() if get_login_type().get('third', '').get('wxp'): code, qr_info = make_wx_login_qrcode() - return wx_qr_code_response(ret, code, qr_info) + return wx_qr_code_response(ret, code, qr_info, get_real_ip_address(request)) return Response(ret.dict) @@ -758,12 +758,12 @@ class WeChatLoginCheckView(APIView): ticket = request.data.get("ticket") if ticket: wx_ticket_data = get_wx_ticket_login_info_cache(ticket) - if wx_ticket_data: - if wx_ticket_data == -1: + if wx_ticket_data and wx_ticket_data.get('pk'): + if wx_ticket_data.get('pk', -1) == -1: ret.msg = "还未绑定用户,请通过手机或者邮箱登录账户之后进行绑定" ret.code = 1005 else: - user = UserInfo.objects.filter(pk=wx_ticket_data).first() + user = UserInfo.objects.filter(pk=wx_ticket_data['pk']).first() if user.is_active: key, user_info = set_user_token(user) serializer = UserInfoSerializer(user_info) diff --git a/fir_ser/api/views/thirdlogin.py b/fir_ser/api/views/thirdlogin.py index c195f03..88472e9 100644 --- a/fir_ser/api/views/thirdlogin.py +++ b/fir_ser/api/views/thirdlogin.py @@ -5,19 +5,21 @@ # date: 2021/3/29 from rest_framework.pagination import PageNumberPagination from rest_framework.response import Response +from django.http.response import HttpResponse import logging import random from rest_framework.views import APIView from rest_framework_xml.parsers import XMLParser -from api.models import ThirdWeChatUserInfo, UserInfo +from api.models import ThirdWeChatUserInfo, UserInfo, UserCertificationInfo from api.utils.auth import ExpiringTokenAuthentication from api.utils.mp.chat import reply, receive -from api.utils.mp.wechat import check_signature, WxMsgCrypt, get_userinfo_from_openid +from api.utils.mp.wechat import check_signature, WxMsgCrypt, get_userinfo_from_openid, WxTemplateMsg from api.utils.response import BaseResponse from api.utils.serializer import ThirdWxSerializer -from api.utils.storage.caches import set_wx_ticket_login_info_cache +from api.utils.storage.caches import set_wx_ticket_login_info_cache, get_wx_ticket_login_info_cache from api.views.login import get_login_type +from config import WEB_DOMAIN logger = logging.getLogger(__name__) @@ -43,15 +45,27 @@ def reply_login_msg(rec_msg, to_user, from_user, ): content = f'还未绑定用户,请通过手机或者邮箱登录账户之后进行绑定' u_data_id = -1 wx_user_obj = ThirdWeChatUserInfo.objects.filter(openid=to_user).first() + + wx_ticket_info = get_wx_ticket_login_info_cache(rec_msg.Ticket) + if wx_user_obj: u_data_id = wx_user_obj.user_id.pk content = f'用户 {wx_user_obj.user_id.first_name} 登录成功' - set_wx_ticket_login_info_cache(rec_msg.Ticket, u_data_id) + WxTemplateMsg().login_success_msg(to_user, wx_user_obj.nickname, wx_user_obj.user_id.first_name) + else: + wx_user_info = update_or_create_wx_userinfo(to_user, None, False) + WxTemplateMsg().login_failed_msg(to_user, wx_user_info.get('nickname', '')) + + if wx_ticket_info and wx_ticket_info.get('ip_addr'): + ip_addr = wx_ticket_info.get('ip_addr') + logger.info(f"{content} ip:{ip_addr}") + + set_wx_ticket_login_info_cache(rec_msg.Ticket, {'pk': u_data_id}) reply_msg = reply.TextMsg(to_user, from_user, content) return reply_msg.send() -def update_or_create_wx_userinfo(to_user, user_obj): +def update_or_create_wx_userinfo(to_user, user_obj, create=True): code, wx_user_info = get_userinfo_from_openid(to_user) logger.info(f"get openid:{to_user} info:{to_user} code:{code}") if code: @@ -64,7 +78,9 @@ def update_or_create_wx_userinfo(to_user, user_obj): 'address': f"{wx_user_info.get('country')}-{wx_user_info.get('province')}-{wx_user_info.get('city')}", 'subscribe': wx_user_info.get('subscribe'), } + if create: ThirdWeChatUserInfo.objects.update_or_create(user_id=user_obj, openid=to_user, defaults=wx_user_info) + return wx_user_info class ValidWxChatToken(APIView): @@ -72,7 +88,7 @@ class ValidWxChatToken(APIView): def get(self, request): params = request.query_params - return Response(check_signature(params)) + return HttpResponse(check_signature(params)) def post(self, request): params = request.query_params @@ -100,7 +116,8 @@ class ValidWxChatToken(APIView): result = reply_msg.send() else: result = reply.Msg().send() - + logger.info(f"replay msg: {result}") + return HttpResponse(result) elif isinstance(rec_msg, receive.EventMsg): to_user = rec_msg.FromUserName from_user = rec_msg.ToUserName @@ -109,6 +126,57 @@ class ValidWxChatToken(APIView): content = random.choices(GOOD_XX)[0] reply_msg = reply.TextMsg(to_user, from_user, content) result = reply_msg.send() + logger.info(f"replay msg: {result}") + return HttpResponse(result) + elif rec_msg.Eventkey == 'flyapps': + reply_msg = reply.TextMsg(to_user, from_user, WEB_DOMAIN) + result = reply_msg.send() + logger.info(f"replay msg: {result}") + return HttpResponse(result) + elif rec_msg.Eventkey in ['query_bind', 'unbind']: + wx_user_obj = ThirdWeChatUserInfo.objects.filter(openid=to_user).first() + if rec_msg.Eventkey == 'query_bind': + if wx_user_obj: + user_obj = wx_user_obj.user_id + content = f'绑定用户 {user_obj.first_name}\n' + if user_obj.email: + content += f' 登录邮箱:{user_obj.email}\n' + if user_obj.mobile: + content += f' 登录手机:{user_obj.mobile}' + if content.endswith('\n'): + content = content[0:-1] + user_cert_obj = UserCertificationInfo.objects.filter(user_id=user_obj, + status=1).first() + if user_cert_obj: + name = user_cert_obj.name + else: + name = user_obj.first_name + WxTemplateMsg().bind_query_success_msg(to_user, wx_user_obj.nickname, + user_obj.first_name, name, user_obj.mobile, + user_obj.email) + else: + content = '暂无登录绑定信息' + wx_user_info = update_or_create_wx_userinfo(to_user, None, False) + WxTemplateMsg().query_bind_info_failed_msg(to_user, wx_user_info.get('nickname'), + "查询登录绑定", content) + + + elif rec_msg.Eventkey == 'unbind': + if wx_user_obj: + content = f'解绑用户 {wx_user_obj.user_id.first_name} 成功' + WxTemplateMsg().unbind_success_msg(to_user, wx_user_obj.nickname, + wx_user_obj.user_id.first_name) + ThirdWeChatUserInfo.objects.filter(openid=to_user).delete() + else: + content = f'暂无登录绑定信息' + wx_user_info = update_or_create_wx_userinfo(to_user, None, False) + WxTemplateMsg().query_bind_info_failed_msg(to_user, wx_user_info.get('nickname'), + "解除登录绑定", content) + + logger.info(f"to_user:{to_user} from_user:{from_user} reply msg: {content}") + reply_msg = reply.TextMsg(to_user, from_user, content) + result = reply_msg.send() + elif rec_msg.Event in ['subscribe', 'unsubscribe']: # 订阅 reply_msg = reply.TextMsg(to_user, from_user, content) result = reply_msg.send() @@ -129,19 +197,23 @@ class ValidWxChatToken(APIView): if user_obj and user_obj.uid == wx_user_obj.user_id.uid: content = f'账户 {wx_user_obj.user_id.first_name} 已经绑定成功,感谢您的使用' update_or_create_wx_userinfo(to_user, user_obj) + WxTemplateMsg().bind_success_msg(to_user, wx_user_obj.nickname, user_obj.first_name) else: content = f'账户已经被 {wx_user_obj.user_id.first_name} 绑定' + WxTemplateMsg().bind_failed_msg(to_user, wx_user_obj.nickname, content) else: - update_or_create_wx_userinfo(to_user, user_obj) + wx_user_info = update_or_create_wx_userinfo(to_user, user_obj) content = f'账户绑定 {wx_user_obj.user_id.first_name} 成功' - set_wx_ticket_login_info_cache(rec_msg.Ticket, user_obj.pk) + WxTemplateMsg().bind_success_msg(to_user, wx_user_info.get('nickname', ''), + user_obj.first_name) + + set_wx_ticket_login_info_cache(rec_msg.Ticket, {'pk': user_obj.pk}) reply_msg = reply.TextMsg(to_user, from_user, content) result = reply_msg.send() - else: logger.error('密文解密失败') - - return Response(result) + logger.info(f"replay msg: {result}") + return HttpResponse("success") class PageNumber(PageNumberPagination):