import datetime import json import re import time import requests import urllib import xml.etree.ElementTree from urllib.parse import urljoin from django.db.models import Q from django_filters.rest_framework import DjangoFilterBackend from django_redis import get_redis_connection from rest_framework import filters from rest_framework import viewsets from django.views.decorators.csrf import csrf_exempt from django.http.response import JsonResponse, HttpResponse # from rest_framework.response import Response from django.utils import timezone from dspt_api.util.handle_goods import format_goods from dspt_api.util.random_params import random_params from dspt_api.util.sign import Sign from dspt_api.models import EcChannel, EcEnv, EcApi, EcApiParams, EcCinemaIds, EcRequestLog, EcApiGroup from dspt_api.serializers import EcChannelSerializer, EcEnvSerializer, EcApiSerializer, EcApiParamsSerializer, \ EcCinemaIdsSerializer, EcRequestLogSerializer, EcApiGroupSerializer from dspt_api.util.suggest_params import suggest_params, suggest_params_timestamp from dspt_api.util.general.handle_xml_resp import HandleXmlResp from dspt_api.util.general.format_xml import format_xml # Create your views here. class EcChannelViewSet(viewsets.ModelViewSet): queryset = EcChannel.objects.all() serializer_class = EcChannelSerializer class EcEnvViewSet(viewsets.ModelViewSet): queryset = EcEnv.objects.all() serializer_class = EcEnvSerializer class EcApiViewSet(viewsets.ModelViewSet): queryset = EcApi.objects.order_by('order').all() serializer_class = EcApiSerializer filter_backends = (DjangoFilterBackend,) # http://172.16.1.114:8000/ec/get_api?type=nonmember filterset_fields = ('type',) class EcApiParamsViewSet(viewsets.ModelViewSet): queryset = EcApiParams.objects.all() serializer_class = EcApiParamsSerializer filter_backends = (DjangoFilterBackend,) # http://172.16.1.114:8000/ec/get_api_params?api_id=1 filterset_fields = ('api_id',) class EcCinemaIdsViewSet(viewsets.ModelViewSet): queryset = EcCinemaIds.objects.all() serializer_class = EcCinemaIdsSerializer class EcRequestLogViewSet(viewsets.ModelViewSet): queryset = EcRequestLog.objects.all() serializer_class = EcRequestLogSerializer class EcApiGroupViewSet(viewsets.ModelViewSet): queryset = EcApiGroup.objects.all() serializer_class = EcApiGroupSerializer filter_backends = (DjangoFilterBackend,) # http://172.16.1.114:8000/ec/get_api_group?type=nonmember filterset_fields = ('type',) # 内部方法,用于生成requests请求对象 def handle_request(_request): """ POST请求参数 env dspt或zyds member_type member或nonmember api api地址 params 请求参数 """ req = json.loads(_request.body) env = req.get('env') member_type = req.get('member_type') api = req.get('api') params = json.loads(req.get('params')) resp_format = params['format'] try: base_url = EcEnv.objects.filter(Q(code=env) & Q(type=member_type)).values('host').first()['host'] except Exception as e: return False, e pid = params.get('pid') try: key = EcChannel.objects.filter(Q(pid=pid) & Q(env=env)).values('channel_key').first()['channel_key'] except Exception as e: return False, e sign = Sign(key) req_params = sign.add_sig(params) req_obj = prepare_request(base_url, api, req_params) return req_obj, req_params['_sig'] # 内部方法,用于拼接请求 def prepare_request(_url, _api, _params): req = requests.PreparedRequest() req.prepare_method('GET') req.prepare_url(url=urljoin(_url, _api), params=_params) req.prepare_headers({'host': _url[7:]}) return req @csrf_exempt def get_api_params_by_api_type(request): """ 对比接口用于获取指定类型的接口参数 http://172.16.1.168:8000/ec/get_params_by_type?type=nonmember """ # 通过Api model获取会员或非会员的api id _type = request.GET.get('type') api_list = EcApi.objects.filter(type=_type).values('id') api_id = [api['id'] for api in api_list] # 通过 api id 查询对应api的参数 api_params = EcApiParams.objects.filter(api_id__in=api_id) params_data = EcApiParamsSerializer(api_params, many=True).data # 组织数据结构,根据api id分组 api_params_dict = {} for params in params_data: if params['api_id'] in api_params_dict.keys(): api_params_dict[params['api_id']].append(params) else: api_params_dict[params['api_id']] = [params] return JsonResponse(api_params_dict, json_dumps_params={'ensure_ascii': False}) # 通过接口获取推荐参数的入口函数 @csrf_exempt def get_suggest_params_by_api(request): # 获取基础数据 member_type = request.GET.get('member_type') api = request.GET.get('api') user_ip = request.META.get('REMOTE_ADDR') params = suggest_params(member_type, api, user_ip) return JsonResponse(params, safe=False) @csrf_exempt def get_suggest_params_timestamp_by_api(request): # 获取基础数据 member_type = request.GET.get('member_type') api = request.GET.get('api') user_ip = request.META.get('REMOTE_ADDR') _ts = suggest_params_timestamp(member_type, api, user_ip) print('timestamp', _ts) return JsonResponse({'timestamp': _ts}) # 外部接口, 用于实时生成url并返回前端 @csrf_exempt def general_api_url(request): """ 此接口为POST接口 request 包含 env dspt或zyds member_type member或nonmember api api地址 params 请求参数 """ # req = json.loads(request.body) # params = json.loads(req.get('params')) # resp_format = params['format'] req_obj, sig = handle_request(request) failed_resp_data = {'url': '请检查foramt、pid参数', 'sig': ''} result = {'url': urllib.parse.unquote(req_obj.url), 'sig': sig} if req_obj is False: return JsonResponse(failed_resp_data) return JsonResponse(result) # 对外接口,用于收集用户提交内容后发送请求 @csrf_exempt def send_request(request): """ request 包含 1. 请求链接 request_url 2. 返回数据 response_data 3. 接口 api 4. 环境 env """ # 获取返回类型结果 req = json.loads(request.body) params = json.loads(req.get('params')) resp_format = params['format'] member_type = req.get('member_type') # 初始化redis redis_conn = get_redis_connection() user_ip = request.META.get('REMOTE_ADDR') web_req = json.loads(request.body) api = web_req.get('api') redis_key_api = f'dspt_api_{user_ip}_{member_type}_{api}' # 发送请求 req, sig = handle_request(request) failed_resp_data = {'url': '请检查foramt、pid参数', 'sig': ''} if req is False: return JsonResponse(failed_resp_data) response = requests.Session().send(req) print('response', response.content) # 处理xml数据 handled_data = '' response_data = '' if resp_format == 'json': response_data = response.text handled_data = json.loads(response.text) if resp_format == 'xml': response_data = format_xml(re.sub(r'>\n?\s+?<', r'>\n<', response.text)) handled_data = HandleXmlResp(response.text).format_xml() # 卖品接口特殊处理去掉无用cate一层结构 if api == 'cinema/goods': handled_data = format_goods(handled_data) # 记录Redis if redis_conn.exists(redis_key_api): redis_conn.delete(redis_key_api) data = { 'request_url': req.url, 'member_type': member_type, 'format': resp_format, 'timestamp': int(time.time() * 1000), 'params': params, 'response_data': response.text, 'handled_data': handled_data, } print(data) redis_conn.set(redis_key_api, json.dumps(data), 10 * 60 * 60) # 插入数据 db_data = { 'ip': user_ip, 'env': web_req.get('env'), 'request': req.url, 'response': response.content, 'request_datetime': timezone.now(), } print(db_data) EcRequestLog.objects.create(**db_data) # 写入随机值 user_data = { 'user_ip': user_ip, 'member_type': member_type, 'api': api, 'format': resp_format, } random_params(user_data, handled_data) return JsonResponse({'format': resp_format, 'data': response_data, 'handled': json.dumps(handled_data)}) # 对外接口,用于收集用户选择的数据,例如场次,卖品等,以便后续接口直接调用 @csrf_exempt def set_user_select_data(request): """ 参数 包含 1. api地址 2. 接口类型 会员 非会员 3. 接口数据类型 json xml 4. 用户选择数据 """ # 获取返回类型结果 req = json.loads(request.body) api = req.get('api') member_type = req.get('member_type') resp_format = 'xml' if req.get('format') == 'html' else req.get('format') user_data = req.get('user_data') user_ip = request.META.get('REMOTE_ADDR') # 初始化redis redis_conn = get_redis_connection() redis_key_user_data = f'dspt_api_{user_ip}_{member_type}_{api}_user_data' print('user_data', user_data) # 记录用户选择 data = { 'api': api, 'member_type': member_type, 'format': resp_format, 'timestamp': int(time.time() * 1000), 'user_data': [json.loads(user_data)], } print('set_user_select_data', data) if redis_conn.exists(redis_key_user_data): redis_conn.delete(redis_key_user_data) if redis_conn.set(redis_key_user_data, json.dumps(data), 10 * 60 * 60): return JsonResponse({'result': 'success'}) return JsonResponse({'result': 'fail'}) @csrf_exempt def clear_user_select_data(request): """ 参数 包含 1. api地址 2. 接口类型 会员 非会员 """ # 获取返回类型结果 req = json.loads(request.body) api = req.get('api') member_type = req.get('member_type') user_ip = request.META.get('REMOTE_ADDR') # 初始化redis redis_conn = get_redis_connection() redis_key_user_data = f'dspt_api_{user_ip}_{member_type}_{api}_user_data' if redis_conn.exists(redis_key_user_data): if redis_conn.delete(redis_key_user_data): return JsonResponse({'result': 'success'}) return JsonResponse({'result': 'fail'}) # 对外接口,用于给前端提供上次请求的数据,如果没有则返回数据库中的默认值 # 若果有些字段是依赖于其他接口返回值生成的则单独处理此接口, 单独处理逻辑在util目录下 @csrf_exempt def get_last_request_data(request): redis_conn = get_redis_connection() user_ip = request.META.get('REMOTE_ADDR') api = request.get('api') match api: case '': pass case _: pass