From d8b2ac28a344ae3bfcc87b88fb9d98a877a32a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E5=89=8D=E6=98=8E?= <909355014@qq.com> Date: Tue, 26 Jul 2022 12:21:53 +0800 Subject: [PATCH] =?UTF-8?q?tlog=20=E5=A2=9E=E5=8A=A0errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/am/tasks.py | 2 +- apps/auth1/serializers.py | 4 + apps/auth1/views.py | 22 ++-- apps/develop/serializers.py | 16 +++ apps/develop/tasks.py | 15 +++ apps/develop/urls.py | 10 +- apps/develop/views.py | 85 +++++++++++++- apps/ecm/tasks.py | 4 +- apps/opm/tasks.py | 2 +- apps/rpm/tasks.py | 2 +- apps/system/tasks.py | 2 +- apps/system/views.py | 22 ++-- apps/third/migrations/0009_tlog_errors.py | 18 +++ apps/third/models.py | 1 + apps/utils/errors.py | 1 + apps/utils/serializers.py | 15 --- apps/utils/{task.py => tasks.py} | 1 + apps/utils/urls.py | 3 +- apps/utils/views.py | 52 +-------- apps/utils/wx.py | 131 ++++++++++++++++++++++ apps/utils/wxmp.py | 106 +++++++++++++++++ apps/vm/tasks.py | 2 +- apps/wf/tasks.py | 2 +- server/settings.py | 5 + 24 files changed, 428 insertions(+), 95 deletions(-) create mode 100644 apps/third/migrations/0009_tlog_errors.py rename apps/utils/{task.py => tasks.py} (86%) create mode 100644 apps/utils/wx.py create mode 100644 apps/utils/wxmp.py diff --git a/apps/am/tasks.py b/apps/am/tasks.py index 81b67363..f13525f7 100644 --- a/apps/am/tasks.py +++ b/apps/am/tasks.py @@ -1,5 +1,5 @@ from __future__ import absolute_import, unicode_literals -from apps.utils.task import CustomTask +from apps.utils.tasks import CustomTask from apps.am.models import Area from celery import shared_task from django.core.cache import cache diff --git a/apps/auth1/serializers.py b/apps/auth1/serializers.py index 6c12c372..d0bc37ec 100755 --- a/apps/auth1/serializers.py +++ b/apps/auth1/serializers.py @@ -13,3 +13,7 @@ class SendCodeSerializer(serializers.Serializer): class CodeLoginSerializer(serializers.Serializer): phone = serializers.CharField(label="手机号") code = serializers.CharField(label="验证码") + + +class WxCodeSerializer(serializers.Serializer): + code = serializers.CharField(label="code") \ No newline at end of file diff --git a/apps/auth1/views.py b/apps/auth1/views.py index d5468ddf..2a622b19 100755 --- a/apps/auth1/views.py +++ b/apps/auth1/views.py @@ -15,9 +15,11 @@ 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.utils.wxmp import wxmpClient +from apps.utils.wx import wxClient -from apps.auth1.serializers import LoginSerializer, SendCodeSerializer +from apps.auth1.serializers import CodeLoginSerializer, LoginSerializer, SendCodeSerializer, WxCodeSerializer from apps.system.models import User from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet @@ -90,12 +92,11 @@ class WxmpLogin(CreateAPIView): """ authentication_classes = [] permission_classes = [] + serializer_class = WxCodeSerializer 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) + info = wxmpClient.get_basic_info(code=code) openid = info['openid'] session_key = info['session_key'] try: @@ -116,23 +117,22 @@ class WxLogin(CreateAPIView): """ authentication_classes = [] permission_classes = [] + serializer_class = WxCodeSerializer 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) + info = wxClient.get_basic_info(code=code) openid = info['openid'] - session_key = info['session_key'] + access = info['access_token'] 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_token'] = access 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) + return Response({'wx_openid': openid, 'wx_token': access}, status=400) class GetTokenFromCache(CreateAPIView): @@ -142,6 +142,7 @@ class GetTokenFromCache(CreateAPIView): """ authentication_classes = [] permission_classes = [] + serializer_class = WxCodeSerializer def post(self, request): code = request.data['code'] @@ -175,6 +176,7 @@ class CodeLogin(CreateAPIView): """ authentication_classes = [] permission_classes = [] + serializer_class = CodeLoginSerializer def post(self, request): phone = request.data['phone'] diff --git a/apps/develop/serializers.py b/apps/develop/serializers.py index e69de29b..2d28638a 100755 --- a/apps/develop/serializers.py +++ b/apps/develop/serializers.py @@ -0,0 +1,16 @@ +from rest_framework import serializers + + +class SendSmsSerializer(serializers.Serializer): + phone = serializers.CharField(label='手机号') + template_code = serializers.CharField(label='模板标识') + template_param = serializers.JSONField(label='模板参数') + + +class GenerateVoiceSerializer(serializers.Serializer): + msg = serializers.CharField(label='文本') + + +class TestTaskSerializer(serializers.Serializer): + args = serializers.ListField(child=serializers.CharField(), label='列表参数', required=False, allow_null=True) + kwargs = serializers.JSONField(label="字典参数", required=False, allow_null=True) diff --git a/apps/develop/tasks.py b/apps/develop/tasks.py index 51c8c224..86779f65 100755 --- a/apps/develop/tasks.py +++ b/apps/develop/tasks.py @@ -1,7 +1,12 @@ from __future__ import absolute_import, unicode_literals from celery import shared_task import subprocess +from apps.utils.tasks import CustomTask from server.settings import DATABASES, BACKUP_PATH, SH_PATH, SD_PWD +import requests +import json +from django.conf import settings +from django.core.cache import cache @shared_task @@ -37,6 +42,7 @@ def reload_web_git(): if completed.returncode != 0: return completed.stderr + @shared_task def reload_server_only(): command = 'echo "{}" | sudo -S supervisorctl reload'.format(SD_PWD) @@ -49,3 +55,12 @@ def backup_media(): command = 'bash {}/backup_media.sh'.format(SH_PATH) completed = subprocess.run(command, shell=True, capture_output=True, text=True) return completed + + +@shared_task(base=CustomTask) +def get_wx_token(): + r = requests.get('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}'.format( + settings.WX_APPID, settings.WX_APPSECRET)).text + token = json.loads(r)['access_token'] + cache.set('wx_token', token) + return token diff --git a/apps/develop/urls.py b/apps/develop/urls.py index 1e60ddab..096b3e1e 100755 --- a/apps/develop/urls.py +++ b/apps/develop/urls.py @@ -1,13 +1,17 @@ -from django.urls import path -from apps.develop.views import BackupDatabase, BackupMedia, ReloadClientGit, ReloadServerGit, ReloadServerOnly +from django.urls import path, include +from apps.develop.views import BackupDatabase, BackupMedia, ReloadClientGit, ReloadServerGit, ReloadServerOnly, TestViewSet +from rest_framework.routers import DefaultRouter API_BASE_URL = 'api/develop/' HTML_BASE_URL = 'develop/' +router = DefaultRouter() +router.register('test', TestViewSet, basename='api_test') urlpatterns = [ path(API_BASE_URL + 'reload_server_git/', ReloadServerGit.as_view()), path(API_BASE_URL + 'reload_web_git/', ReloadClientGit.as_view()), path(API_BASE_URL + 'reload_server_only/', ReloadServerOnly.as_view()), path(API_BASE_URL + 'backup_database/', BackupDatabase.as_view()), - path(API_BASE_URL + 'backup_media/', BackupMedia.as_view()) + path(API_BASE_URL + 'backup_media/', BackupMedia.as_view()), + path(API_BASE_URL, include(router.urls)), ] diff --git a/apps/develop/views.py b/apps/develop/views.py index fe90ecad..952ee1b6 100755 --- a/apps/develop/views.py +++ b/apps/develop/views.py @@ -1,8 +1,20 @@ +import json from rest_framework.views import APIView from rest_framework.permissions import IsAdminUser from rest_framework.response import Response -from apps.develop.tasks import backup_database, backup_media, reload_web_git, reload_server_git, reload_server_only +from rest_framework.serializers import Serializer +from rest_framework.decorators import action +from apps.develop.serializers import GenerateVoiceSerializer, SendSmsSerializer, TestTaskSerializer +from apps.develop.tasks import backup_database, backup_media, get_wx_token, reload_web_git, reload_server_git, reload_server_only from rest_framework.exceptions import APIException, ParseError +from apps.system.models import User +from apps.system.tasks import show +from apps.utils.sms import send_sms +from apps.utils.speech import generate_voice +from django.core.cache import cache +import requests + +from apps.utils.viewsets import CustomGenericViewSet # Create your views here. @@ -91,3 +103,74 @@ class BackupMedia(APIView): return Response() else: raise APIException(completed.stdout) + + +class TestViewSet(CustomGenericViewSet): + authentication_classes = () + permission_classes = () + + @action(methods=['post'], detail=False, serializer_class=SendSmsSerializer) + def send_sms(self, request, pk=None): + """发送短信测试 + + 发送短信测试 + """ + serializer = SendSmsSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + vdata = serializer.validated_data + res = send_sms(**vdata) + return Response(res) + + @action(methods=['post'], detail=False, serializer_class=GenerateVoiceSerializer) + def generate_voice(self, request, pk=None): + """文字转语音测试 + + 文字转语音测试 + """ + serializer = GenerateVoiceSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + vdata = serializer.validated_data + res = generate_voice(**vdata) + return Response(res) + + @action(methods=['post'], detail=False, serializer_class=TestTaskSerializer) + def task(self, request, pk=None): + """任务派发测试 + + 任务派发测试 + """ + serializer = TestTaskSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + vdata = serializer.validated_data + ret = {} + task = show.delay(*vdata.get('args', []), **vdata.get('kwargs', {})) + ret['task_id'] = task.task_id + return Response(ret) + + @action(methods=['post'], detail=False, serializer_class=Serializer) + def send_wx_msg(self, request, pk=None): + """微信模板消息发送测试 + + 微信模板消息发送测试 + """ + wx_token = cache.get('wx_token', None) + if not wx_token: + wx_token = get_wx_token() + data = { + "touser": 'ocAnm5vab6QGOnf2dqpuUZGJ0xyw', + "template_id": "wczl-hB_bs8CpxwO8_KZuogSTPFXqhBxf8C5HjKl2M4", + "url": "http://weixin.qq.com/download", + # "miniprogram":{ + # "appid":"xiaochengxuappid12345", + # "pagepath":"index?foo=bar" + # }, + "client_msg_id": "MSG_000001", + "data": { + "first": { + "value": "恭喜你购买成功!", + "color": "#173177" + }, + } + } + requests.post(url='https://api.weixin.qq.com/cgi-bin/message/template/send?access_token='+wx_token, json=data) + return Response({'wx_token': wx_token, 'wx_openid': 'ocAnm5vab6QGOnf2dqpuUZGJ0xyw'}) diff --git a/apps/ecm/tasks.py b/apps/ecm/tasks.py index 66c60771..0da37ce0 100644 --- a/apps/ecm/tasks.py +++ b/apps/ecm/tasks.py @@ -49,4 +49,6 @@ def check_event_timeout(): cate = i.cates.all().order_by('priority', 'create_time').first() if cate.hanle_minute > 0 and (timezone.now()-i.create_time).seconds > cate.hanle_minute * 60: i.is_timeout = True - i.save() \ No newline at end of file + i.save() + + diff --git a/apps/opm/tasks.py b/apps/opm/tasks.py index 110de858..6400b302 100644 --- a/apps/opm/tasks.py +++ b/apps/opm/tasks.py @@ -1,7 +1,7 @@ # Create your tasks here from __future__ import absolute_import, unicode_literals from apps.opm.models import Operation, Opl -from apps.utils.task import CustomTask +from apps.utils.tasks import CustomTask from celery import shared_task from apps.wf.models import State, Ticket diff --git a/apps/rpm/tasks.py b/apps/rpm/tasks.py index 386ec9d8..e3bb65a3 100644 --- a/apps/rpm/tasks.py +++ b/apps/rpm/tasks.py @@ -2,5 +2,5 @@ from __future__ import absolute_import, unicode_literals from apps.hrm.models import Certificate, Employee from apps.rpm.models import Rcertificate, Remployee, Rpj, Rpjcertificate, Rpjmember -from apps.utils.task import CustomTask +from apps.utils.tasks import CustomTask from celery import shared_task diff --git a/apps/system/tasks.py b/apps/system/tasks.py index 6ef9a499..793cfe06 100755 --- a/apps/system/tasks.py +++ b/apps/system/tasks.py @@ -1,7 +1,7 @@ # Create your tasks here from __future__ import absolute_import, unicode_literals from random import random, randint -from apps.utils.task import CustomTask +from apps.utils.tasks import CustomTask from celery import shared_task from django.core.mail import send_mail diff --git a/apps/system/views.py b/apps/system/views.py index 82316e7f..67be354e 100755 --- a/apps/system/views.py +++ b/apps/system/views.py @@ -476,11 +476,12 @@ class UserViewSet(CustomModelViewSet): 绑定微信小程序 """ 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() + if 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]) @@ -502,11 +503,12 @@ class UserViewSet(CustomModelViewSet): 绑定微信公众号, 用于发送通知 """ 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() + if 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}) diff --git a/apps/third/migrations/0009_tlog_errors.py b/apps/third/migrations/0009_tlog_errors.py new file mode 100644 index 00000000..1c8690fd --- /dev/null +++ b/apps/third/migrations/0009_tlog_errors.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2022-07-26 04:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('third', '0008_auto_20220713_1408'), + ] + + operations = [ + migrations.AddField( + model_name='tlog', + name='errors', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/apps/third/models.py b/apps/third/models.py index 02d13781..757cd807 100755 --- a/apps/third/models.py +++ b/apps/third/models.py @@ -76,5 +76,6 @@ class Tlog(BaseModel): response = models.JSONField(null=True, blank=True) method = models.CharField(max_length=10) url = models.TextField(null=True, blank=True) + errors = models.TextField(null=True, blank=True) params = models.JSONField(null=True, blank=True) body = models.JSONField(null=True, blank=True) diff --git a/apps/utils/errors.py b/apps/utils/errors.py index b518d00d..217cbeca 100755 --- a/apps/utils/errors.py +++ b/apps/utils/errors.py @@ -1,2 +1,3 @@ SIGN_MAKE_FAIL = {"code": "sign_make_fail", "detail": "签名照生成失败,请重新上传"} PKS_ERROR = {"code": "pks_error", "detail": "未获取到主键列表"} +WX_REQUEST_ERROR = {"code": "wx_request_error", "detail": "微信接口访问异常"} diff --git a/apps/utils/serializers.py b/apps/utils/serializers.py index 261f6c81..ca02634b 100755 --- a/apps/utils/serializers.py +++ b/apps/utils/serializers.py @@ -39,18 +39,3 @@ class CustomModelSerializer(DynamicFieldsMixin, serializers.ModelSerializer): if hasattr(instance, 'update_by'): validated_data['update_by'] = getattr(self.request, 'user', None) return super().update(instance, validated_data) - - -class SendSmsSerializer(serializers.Serializer): - phone = serializers.CharField(label='手机号') - template_code = serializers.CharField(label='模板标识') - template_param = serializers.JSONField(label='模板参数') - - -class GenerateVoiceSerializer(serializers.Serializer): - msg = serializers.CharField(label='文本') - - -class TestTaskSerializer(serializers.Serializer): - args = serializers.ListField(child=serializers.CharField(), label='列表参数', required=False, allow_null=True) - kwargs = serializers.JSONField(label="字典参数", required=False, allow_null=True) diff --git a/apps/utils/task.py b/apps/utils/tasks.py similarity index 86% rename from apps/utils/task.py rename to apps/utils/tasks.py index 9a200e86..5f8d5dd2 100644 --- a/apps/utils/task.py +++ b/apps/utils/tasks.py @@ -1,3 +1,4 @@ +# from __future__ import absolute_import, unicode_literals from celery import Task import logging diff --git a/apps/utils/urls.py b/apps/utils/urls.py index 0df76e26..325c391f 100755 --- a/apps/utils/urls.py +++ b/apps/utils/urls.py @@ -1,11 +1,10 @@ from django.urls import path, include from rest_framework import routers -from apps.utils.views import SignatureViewSet, TestViewSet +from apps.utils.views import SignatureViewSet API_BASE_URL = 'api/utils/' router = routers.DefaultRouter() router.register('signature', SignatureViewSet, basename='signature') -router.register('test', TestViewSet, basename='util_test') urlpatterns = [ path(API_BASE_URL, include(router.urls)), diff --git a/apps/utils/views.py b/apps/utils/views.py index 56f81de3..ba000b54 100755 --- a/apps/utils/views.py +++ b/apps/utils/views.py @@ -4,18 +4,19 @@ import cv2 from django.http import HttpResponse from apps.system.tasks import show from apps.utils.errors import SIGN_MAKE_FAIL -from apps.utils.sms import send_sms -from apps.utils.speech import generate_voice from server.settings import BASE_DIR import numpy as np from rest_framework.response import Response from rest_framework.exceptions import ParseError from apps.utils.viewsets import CustomGenericViewSet from apps.utils.mixins import CustomCreateModelMixin -from apps.utils.serializers import GenSignatureSerializer, GenerateVoiceSerializer, SendSmsSerializer, TestTaskSerializer +from apps.utils.serializers import GenSignatureSerializer from rest_framework.views import APIView from rest_framework.decorators import action from rest_framework.serializers import Serializer +from django.core.cache import cache +import json +import requests class SignatureViewSet(CustomCreateModelMixin, CustomGenericViewSet): @@ -54,47 +55,4 @@ class SignatureViewSet(CustomCreateModelMixin, CustomGenericViewSet): cv2.imwrite(new_path, image) return Response({'path': new_path.replace(BASE_DIR, '')}) except Exception: - raise ParseError(**SIGN_MAKE_FAIL) - - -class TestViewSet(CustomGenericViewSet): - authentication_classes = () - permission_classes = () - - @action(methods=['post'], detail=False, serializer_class=SendSmsSerializer) - def send_sms(self, request, pk=None): - """发送短信测试 - - 发送短信测试 - """ - serializer = SendSmsSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - vdata = serializer.validated_data - res = send_sms(**vdata) - return Response(res) - - @action(methods=['post'], detail=False, serializer_class=GenerateVoiceSerializer) - def generate_voice(self, request, pk=None): - """文字转语音测试 - - 文字转语音测试 - """ - serializer = GenerateVoiceSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - vdata = serializer.validated_data - res = generate_voice(**vdata) - return Response(res) - - @action(methods=['post'], detail=False, serializer_class=TestTaskSerializer) - def task(self, request, pk=None): - """任务派发测试 - - 任务派发测试 - """ - serializer = TestTaskSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - vdata = serializer.validated_data - ret = {} - task = show.delay(*vdata.get('args', []), **vdata.get('kwargs', {})) - ret['task_id'] = task.task_id - return Response(ret) + raise ParseError(**SIGN_MAKE_FAIL) \ No newline at end of file diff --git a/apps/utils/wx.py b/apps/utils/wx.py new file mode 100644 index 00000000..843d67da --- /dev/null +++ b/apps/utils/wx.py @@ -0,0 +1,131 @@ +import json +import time +from threading import Thread +import uuid + +import requests +from django.conf import settings +from rest_framework.exceptions import APIException, ParseError +import logging +from apps.third.models import Tlog +from apps.utils.errors import WX_REQUEST_ERROR +from apps.utils.tools import print_roundtrip +from django.utils.timezone import now +import traceback +requests.packages.urllib3.disable_warnings() + +# 实例化myLogger +myLogger = logging.getLogger('log') + + +class WxClient: + """ + 微信公众号相关 + """ + + def __init__(self, app_id=settings.WX_APPID, + app_secret=settings.WX_APPSECRET) -> None: + if settings.WX_ENABLED: + self.app_id = app_id + self.app_secret = app_secret + self.isRuning = True + self.token = None + self.t = None # 线程 + self.log = {} + self.setup() + + def _get_token_loop(self): + while self.isRuning: + parmas = { + 'grant_type': 'client_credential', + 'appid': self.app_id, + 'secret': self.app_secret + } + _, ret = self.request(url='/cgi-bin/token', params=parmas, method='get') + self.token = ret['access_token'] + time.sleep(3600) + + def setup(self): + t = Thread(target=self._get_token_loop, args=(), daemon=True) + t.start() + + def __del__(self): + """ + 自定义销毁 + """ + self.isRuning = False + # self.t.join() + + def request(self, url: str, method: str, params=dict(), json=dict(), timeout=10, + file_path_rela=None, raise_exception=True): + if not settings.WX_ENABLED: + raise ParseError('微信公众号未启用') + self.log = {"requested_at": now(), "id": uuid.uuid4(), "path": url, "method": method, + "params": params, "body": json, "target": "wx", "result": 10} + files = None + if file_path_rela: # 相对路径 + files = {'file': open(settings.BASE_DIR + file_path_rela, 'rb')} + try: + if params: + url = url.format(**params) + self.log.update({"path": url}) + r = getattr(requests, method)('{}{}'.format('https://api.weixin.qq.com', url), + params=params, json=json, + timeout=timeout, files=files, verify=False) + except Exception: + errors = traceback.format_exc() + myLogger.error(errors) + self.handle_log(result='error', errors=errors) + if raise_exception: + raise APIException(**WX_REQUEST_ERROR) + return 'error', WX_REQUEST_ERROR + # if settings.DEBUG: + # print_roundtrip(r) + if r.status_code == 200: + ret = r.json() + if 'errcode' in ret and ret['errcode'] not in [0, '0']: + detail = '微信错误:' + \ + '{}|{}'.format(str(ret['errcode']), ret.get('errmsg', '')) + err_detail = dict(detail=detail, code='wx_'+str(ret['errcode'])) + self.handle_log(result='fail', response=ret) + if raise_exception: + raise ParseError(**err_detail) + return 'fail', dict(detail=detail, code='wx_'+str(ret['errcode'])) + # self.handle_log(result='success', response=ret) # 成功的日志就不记录了 + return 'success', ret + + self.handle_log(result='error', response=None) + if raise_exception: + raise APIException(**WX_REQUEST_ERROR) + return 'error', WX_REQUEST_ERROR + + def _get_response_ms(self): + """ + Get the duration of the request response cycle is milliseconds. + In case of negative duration 0 is returned. + """ + response_timedelta = now() - self.log["requested_at"] + response_ms = int(response_timedelta.total_seconds() * 1000) + return max(response_ms, 0) + + def handle_log(self, result, response=None, errors=None): + self.log.update({ + "result": result, + "response": response, + "response_ms": self._get_response_ms(), + "errors": errors + }) + Tlog(**self.log).save() + + def get_basic_info(self, code): + params = { + 'appid': self.app_id, + 'secret': self.app_secret, + 'code': code, + 'grant_type': 'authorization_code' + } + _, res = self.request('/sns/oauth2/access_token', params=params, method='get') + return res + + +wxClient = WxClient() diff --git a/apps/utils/wxmp.py b/apps/utils/wxmp.py new file mode 100644 index 00000000..c76fcec8 --- /dev/null +++ b/apps/utils/wxmp.py @@ -0,0 +1,106 @@ +from distutils import errors +import json +import time +from threading import Thread +import uuid + +import requests +from django.conf import settings +from rest_framework.exceptions import APIException, ParseError +import logging +from apps.third.models import Tlog +from apps.utils.errors import WX_REQUEST_ERROR +from apps.utils.tools import print_roundtrip +from django.utils.timezone import now +import traceback +requests.packages.urllib3.disable_warnings() + +# 实例化myLogger +myLogger = logging.getLogger('log') + + +class WxmpClient: + """ + 微信小程序相关 + """ + + def __init__(self, app_id=settings.WXMP_APPID, + app_secret=settings.WXMP_APPSECRET) -> None: + if settings.WXMP_ENABLED: + self.app_id = app_id + self.app_secret = app_secret + self.log = {} + + def request(self, url: str, method: str, params=dict(), json=dict(), timeout=10, + file_path_rela=None, raise_exception=True): + if not settings.WX_ENABLED: + raise ParseError('微信小程序未启用') + self.log = {"requested_at": now(), "id": uuid.uuid4(), "path": url, "method": method, + "params": params, "body": json, "target": "wx", "result": 10} + files = None + if file_path_rela: # 相对路径 + files = {'file': open(settings.BASE_DIR + file_path_rela, 'rb')} + try: + if params: + url = url.format(**params) + self.log.update({"path": url}) + r = getattr(requests, method)('{}{}'.format('https://api.weixin.qq.com', url), + params=params, json=json, + timeout=timeout, files=files, verify=False) + except Exception: + errors = traceback.format_exc() + myLogger.error(errors) + self.handle_log(result='error', errors=errors) + if raise_exception: + raise APIException(**WX_REQUEST_ERROR) + return 'error', WX_REQUEST_ERROR + # if settings.DEBUG: + # print_roundtrip(r) + if r.status_code == 200: + ret = r.json() + if 'errcode' in ret and ret['errcode'] not in [0, '0']: + detail = '微信错误:' + \ + '{}|{}'.format(str(ret['errcode']), ret.get('errmsg', '')) + err_detail = dict(detail=detail, code='wx_'+str(ret['errcode'])) + self.handle_log(result='fail', response=ret) + if raise_exception: + raise ParseError(**err_detail) + return 'fail', dict(detail=detail, code='wx_'+str(ret['errcode'])) + # self.handle_log(result='success', response=ret) # 成功的日志就不记录了 + return 'success', ret + + self.handle_log(result='error', response=None) + if raise_exception: + raise APIException(**WX_REQUEST_ERROR) + return 'error', WX_REQUEST_ERROR + + def _get_response_ms(self): + """ + Get the duration of the request response cycle is milliseconds. + In case of negative duration 0 is returned. + """ + response_timedelta = now() - self.log["requested_at"] + response_ms = int(response_timedelta.total_seconds() * 1000) + return max(response_ms, 0) + + def handle_log(self, result, response=None, errors=None): + self.log.update({ + "result": result, + "response": response, + "response_ms": self._get_response_ms(), + "errors": errors + }) + Tlog(**self.log).save() + + def get_basic_info(self, code): + params = { + 'appid': self.app_id, + 'secret': self.app_secret, + 'js_code': code, + 'grant_type': 'authorization_code' + } + _, res = self.request('/sns/jscode2session', params=params, method='get') + return res + + +wxmpClient = WxmpClient() diff --git a/apps/vm/tasks.py b/apps/vm/tasks.py index 7a0f44d6..76f56fa4 100644 --- a/apps/vm/tasks.py +++ b/apps/vm/tasks.py @@ -1,7 +1,7 @@ # Create your tasks here from __future__ import absolute_import, unicode_literals from apps.hrm.models import Employee -from apps.utils.task import CustomTask +from apps.utils.tasks import CustomTask from apps.vm.models import Visit, Vpeople from apps.wf.models import Ticket from celery import shared_task diff --git a/apps/wf/tasks.py b/apps/wf/tasks.py index b8e26c64..71e5b3b2 100644 --- a/apps/wf/tasks.py +++ b/apps/wf/tasks.py @@ -5,7 +5,7 @@ import logging import traceback from apps.system.models import User from apps.utils.sms import send_sms -from apps.utils.task import CustomTask +from apps.utils.tasks import CustomTask from celery import shared_task from apps.wf.models import State, Ticket, TicketFlow, Transition diff --git a/server/settings.py b/server/settings.py index 1c5082c8..949c2e54 100755 --- a/server/settings.py +++ b/server/settings.py @@ -397,5 +397,10 @@ AI_IP = conf.AI_IP # 微信有关 +WXMP_ENABLED = conf.WXMP_ENABLED WXMP_APPID = conf.WXMP_APPID WXMP_APPSECRET = conf.WXMP_APPSECRET + +WX_ENABLED = conf.WX_ENABLED +WX_APPID = conf.WX_APPID +WX_APPSECRET = conf.WX_APPSECRET