parent
							
								
									a20d3c7dff
								
							
						
					
					
						commit
						f9de02f139
					
				
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						| @ -0,0 +1,44 @@ | |||||||
|  | # Generated by Django 3.0.3 on 2020-03-23 12:20 | ||||||
|  | 
 | ||||||
|  | from django.conf import settings | ||||||
|  | from django.db import migrations, models | ||||||
|  | import django.db.models.deletion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('api', '0012_userinfo_head_img'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='appreleaseinfo', | ||||||
|  |             name='is_master', | ||||||
|  |             field=models.BooleanField(default=True, verbose_name='是否master版本'), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='userinfo', | ||||||
|  |             name='head_img', | ||||||
|  |             field=models.CharField(default='/files/imgs/head_img.jpeg', max_length=256, verbose_name='个人头像'), | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='AppStorage', | ||||||
|  |             fields=[ | ||||||
|  |                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||||||
|  |                 ('name', models.CharField(blank=True, max_length=64, null=True, verbose_name='存储名字')), | ||||||
|  |                 ('storage_type', models.SmallIntegerField(choices=[(0, '本地存储'), (1, '七牛云存储'), (2, '阿里云存储'), (3, '默认存储')], default=3, verbose_name='存储类型')), | ||||||
|  |                 ('access_key', models.CharField(blank=True, max_length=128, null=True, verbose_name='存储访问key')), | ||||||
|  |                 ('secret_key', models.CharField(blank=True, max_length=128, null=True, verbose_name='存储访问secret')), | ||||||
|  |                 ('bucket_name', models.CharField(blank=True, max_length=128, null=True, verbose_name='存储空间bucket_name')), | ||||||
|  |                 ('additionalparameters', models.TextField(blank=True, help_text='阿里云:{"sts_role_arn":"arn信息","endpoint":""}  七牛云:{"domain_name":""}', max_length=256, null=True, verbose_name='额外参数')), | ||||||
|  |                 ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), | ||||||
|  |                 ('description', models.TextField(blank=True, default=None, null=True, verbose_name='备注')), | ||||||
|  |                 ('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户ID')), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 'verbose_name': '存储配置', | ||||||
|  |                 'verbose_name_plural': '存储配置', | ||||||
|  |             }, | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
| @ -0,0 +1,19 @@ | |||||||
|  | # Generated by Django 3.0.3 on 2020-03-23 12:35 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | import django.db.models.deletion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('api', '0013_auto_20200323_2020'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name='userinfo', | ||||||
|  |             name='storage', | ||||||
|  |             field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='app_storage', to='api.AppStorage', verbose_name='存储'), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
| @ -1,192 +0,0 @@ | |||||||
| from django.test import TestCase |  | ||||||
| 
 |  | ||||||
| # # Create your tests here. |  | ||||||
| # import base64 |  | ||||||
| # request_token='ZWZhN2MwNGMwMzg2MWVmMzA5YjUxNDlkMGU0ZWIwNDRiNTlmZWJjYzpuaW5ldmVuQHFxLmNvbQ%3D%3D==' |  | ||||||
| # print(base64.b64decode(request_token)) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # import xml.etree.ElementTree as ET |  | ||||||
| # tree = ET.parse("xmlfir.xml") |  | ||||||
| # root = tree.getroot() |  | ||||||
| # print(root) |  | ||||||
| # print(root.tag)     #data |  | ||||||
| # for child in root: |  | ||||||
| #     print('========>',child.tag,child)  # |  | ||||||
| #     #========> country {'name': 'Liechtenstein'} Liechtenstein |  | ||||||
| #     for i in child: |  | ||||||
| #         print(i.tag,i.attrib,i.text) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| import xml.etree.ElementTree as ET |  | ||||||
| 
 |  | ||||||
| ios_plist = ET.Element("plist", attrib={"version": "1.0"}) |  | ||||||
| dict1 = ET.SubElement(ios_plist, "dict") |  | ||||||
| key1 = ET.SubElement(dict1, "key") |  | ||||||
| key1.text = 'items' |  | ||||||
| array1 = ET.SubElement(dict1, "array") |  | ||||||
| dict2 = ET.SubElement(array1, "dict") |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| et = ET.ElementTree(ios_plist)  # 生成文档对象 |  | ||||||
| # et.write("test.xml", encoding="utf-8", xml_declaration=True) |  | ||||||
| 
 |  | ||||||
| ET.dump(ios_plist)  # 打印生成的格式 |  | ||||||
| 
 |  | ||||||
| import json |  | ||||||
| 
 |  | ||||||
| import xmltodict |  | ||||||
| 
 |  | ||||||
| xjson = json.dumps(xmltodict.parse(""" |  | ||||||
| 
 |  | ||||||
| <plist version="1.0"><dict> |  | ||||||
|   <key>items</key> |  | ||||||
|   <array> |  | ||||||
|     <dict> |  | ||||||
|       <key>assets</key> |  | ||||||
|       <array> |  | ||||||
|         <dict> |  | ||||||
|           <key>kind</key> |  | ||||||
|           <string>software-package</string> |  | ||||||
|           <key>url</key> |  | ||||||
|           <string><![CDATA[https://ali-fir-pro-binary.jappstore.com/52e972d2749a404028e2ab7edb04d7a7f365184d?auth_key=1583720494-0-0-3dc9d1bf4f093c53f1193614ee5f6d3e]]></string> |  | ||||||
|         </dict> |  | ||||||
|         <dict> |  | ||||||
|           <key>kind</key> |  | ||||||
|           <string>display-image</string> |  | ||||||
|           <key>needs-shine</key> |  | ||||||
|           <integer>0</integer> |  | ||||||
|           <key>url</key> |  | ||||||
|           <string><![CDATA[https://ali-fir-pro-icon.fir.im/6be40db9e2bb729db1ce0db93b8ebdba50ea8b4d?auth_key=1583720494-0-0-0212635dc7c21a3c02cd2422d0213e99]]></string> |  | ||||||
|         </dict> |  | ||||||
|         <dict> |  | ||||||
|           <key>kind</key> |  | ||||||
|           <string>full-size-image</string> |  | ||||||
|           <key>needs-shine</key> |  | ||||||
|           <true/> |  | ||||||
|           <key>url</key> |  | ||||||
|           <string><![CDATA[https://ali-fir-pro-icon.fir.im/6be40db9e2bb729db1ce0db93b8ebdba50ea8b4d?auth_key=1583720494-0-0-0212635dc7c21a3c02cd2422d0213e99]]></string> |  | ||||||
|         </dict> |  | ||||||
|       </array> |  | ||||||
|       <key>metadata</key> |  | ||||||
|       <dict> |  | ||||||
|         <key>bundle-identifier</key> |  | ||||||
|         <string>com.fengxin.im.test</string> |  | ||||||
|         <key>bundle-version</key> |  | ||||||
|         <string><![CDATA[1.1.7]]></string> |  | ||||||
|         <key>kind</key> |  | ||||||
|         <string>software</string> |  | ||||||
|         <key>title</key> |  | ||||||
|         <string><![CDATA[亲友圈发发]]></string> |  | ||||||
|       </dict> |  | ||||||
|     </dict> |  | ||||||
|   </array> |  | ||||||
| </dict> |  | ||||||
| </plist> |  | ||||||
| """), indent=4) |  | ||||||
| 
 |  | ||||||
| # print(xjson) |  | ||||||
| # print(xjson["plist"]["dict"]["array"]["dict"]) |  | ||||||
| # Fdata = xjson["plist"]["dict"]["array"]["dict"] |  | ||||||
| # odata = Fdata["array"]["dict"] |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| bin_url="bin_url" |  | ||||||
| img_url="img_url" |  | ||||||
| bundle_id="bundle_id" |  | ||||||
| bundle_version="bundle_version" |  | ||||||
| name="name" |  | ||||||
| 
 |  | ||||||
| ios_plist_tem=""" |  | ||||||
| { |  | ||||||
|     "plist": { |  | ||||||
|         "@version": "1.0", |  | ||||||
|         "dict": { |  | ||||||
|             "key": "items", |  | ||||||
|             "array": { |  | ||||||
|                 "dict": { |  | ||||||
|                     "key": [ |  | ||||||
|                         "assets", |  | ||||||
|                         "metadata" |  | ||||||
|                     ], |  | ||||||
|                     "array": { |  | ||||||
|                         "dict": [ |  | ||||||
|                             { |  | ||||||
|                                 "key": [ |  | ||||||
|                                     "kind", |  | ||||||
|                                     "url" |  | ||||||
|                                 ], |  | ||||||
|                                 "string": [ |  | ||||||
|                                     "software-package", |  | ||||||
|                                     "%s" |  | ||||||
|                                 ] |  | ||||||
|                             }, |  | ||||||
|                             { |  | ||||||
|                                 "key": [ |  | ||||||
|                                     "kind", |  | ||||||
|                                     "needs-shine", |  | ||||||
|                                     "url" |  | ||||||
|                                 ], |  | ||||||
|                                 "string": [ |  | ||||||
|                                     "display-image", |  | ||||||
|                                     "%s" |  | ||||||
|                                 ], |  | ||||||
|                                 "integer": "0" |  | ||||||
|                             }, |  | ||||||
|                             { |  | ||||||
|                                 "key": [ |  | ||||||
|                                     "kind", |  | ||||||
|                                     "needs-shine", |  | ||||||
|                                     "url" |  | ||||||
|                                 ], |  | ||||||
|                                 "string": [ |  | ||||||
|                                     "full-size-image", |  | ||||||
|                                     "%s" |  | ||||||
|                                 ], |  | ||||||
|                                 "true": null |  | ||||||
|                             } |  | ||||||
|                         ] |  | ||||||
|                     }, |  | ||||||
|                     "dict": { |  | ||||||
|                         "key": [ |  | ||||||
|                             "bundle-identifier", |  | ||||||
|                             "bundle-version", |  | ||||||
|                             "kind", |  | ||||||
|                             "title" |  | ||||||
|                         ], |  | ||||||
|                         "string": [ |  | ||||||
|                             "%s", |  | ||||||
|                             "%s", |  | ||||||
|                             "software", |  | ||||||
|                             "%s" |  | ||||||
|                         ] |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| """ %(bin_url,img_url,img_url,bundle_id,bundle_version,name) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # print(xmltodict.unparse(json.loads(ios_plist_tem), indent=4)) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| tokenlist=[ |  | ||||||
|     {'a':{ |  | ||||||
|         'data':['1','fsdf'], |  | ||||||
|         'time':65225 |  | ||||||
|     }} |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| for token in tokenlist: |  | ||||||
|     print(token) |  | ||||||
|     for k,v in token.items(): |  | ||||||
|         print(k,v) |  | ||||||
| @ -1,10 +0,0 @@ | |||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class CommonException(Exception): |  | ||||||
|     def __init__(self,code,msg): |  | ||||||
|         self.error=msg |  | ||||||
|         self.code=code |  | ||||||
| @ -1,23 +0,0 @@ | |||||||
| 
 |  | ||||||
| from api import models |  | ||||||
| from rest_framework.filters import BaseFilterBackend |  | ||||||
| 
 |  | ||||||
| class CourseFilter(BaseFilterBackend): |  | ||||||
|     """ |  | ||||||
|     课程展示 过滤器 |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     def filter_queryset(self,request, queryset, view): |  | ||||||
|         extra = {} |  | ||||||
|         print("request.query_params",request.query_params) |  | ||||||
|         category_id = str(request.query_params.get("category_id")) |  | ||||||
| 
 |  | ||||||
|         # 如果分类ID不是数字或分类ID传输的为0 |  | ||||||
|         print("category_id",category_id) |  | ||||||
|         if not category_id.isdigit() or category_id == "0": |  | ||||||
|             extra = extra |  | ||||||
|         else: |  | ||||||
|             extra.update({"course_category_id":category_id}) |  | ||||||
|         print("extra",extra) |  | ||||||
|         print("queryset.filter(**extra)",queryset.filter(**extra)) |  | ||||||
|         return queryset.filter(**extra) |  | ||||||
| @ -0,0 +1,113 @@ | |||||||
|  | #!/usr/bin/env python | ||||||
|  | # -*- coding:utf-8 -*- | ||||||
|  | # project: 3月 | ||||||
|  | # author: liuyu | ||||||
|  | # date: 2020/3/22 | ||||||
|  | 
 | ||||||
|  | #pip install aliyun-python-sdk-sts oss2 | ||||||
|  | 
 | ||||||
|  | import json | ||||||
|  | import os | ||||||
|  | 
 | ||||||
|  | from aliyunsdkcore import client | ||||||
|  | from aliyunsdksts.request.v20150401 import AssumeRoleRequest | ||||||
|  | 
 | ||||||
|  | import oss2 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # 以下代码展示了STS的用法,包括角色扮演获取临时用户的密钥、使用临时用户的密钥访问OSS。 | ||||||
|  | 
 | ||||||
|  | # STS入门教程请参看  https://yq.aliyun.com/articles/57895 | ||||||
|  | # STS的官方文档请参看  https://help.aliyun.com/document_detail/28627.html | ||||||
|  | 
 | ||||||
|  | # 首先初始化AccessKeyId、AccessKeySecret、Endpoint等信息。 | ||||||
|  | # 通过环境变量获取,或者把诸如“<你的AccessKeyId>”替换成真实的AccessKeyId等。 | ||||||
|  | # 注意:AccessKeyId、AccessKeySecret为子用户的密钥。 | ||||||
|  | # 子用户需要有  调用STS服务AssumeRole接口的权限 | ||||||
|  | # 创建ram用户,授权 管理对象存储服务(OSS)权限 | ||||||
|  | # RoleArn可以在控制台的“访问控制  > RAM角色管理  > 创建的ram用户  > 基本信息  > Arn”上查看。 | ||||||
|  | # | ||||||
|  | # 以杭州区域为例,Endpoint可以是: | ||||||
|  | #   http://oss-cn-hangzhou.aliyuncs.com | ||||||
|  | #   https://oss-cn-hangzhou.aliyuncs.com | ||||||
|  | # 分别以HTTP、HTTPS协议访问。 | ||||||
|  | 
 | ||||||
|  | class StsToken(object): | ||||||
|  |     """AssumeRole返回的临时用户密钥 | ||||||
|  |     :param str access_key_id: 临时用户的access key id | ||||||
|  |     :param str access_key_secret: 临时用户的access key secret | ||||||
|  |     :param int expiration: 过期时间,UNIX时间,自1970年1月1日UTC零点的秒数 | ||||||
|  |     :param str security_token: 临时用户Token | ||||||
|  |     :param str request_id: 请求ID | ||||||
|  |     """ | ||||||
|  |     def __init__(self): | ||||||
|  |         self.access_key_id = '' | ||||||
|  |         self.access_key_secret = '' | ||||||
|  |         self.expiration = 3600 | ||||||
|  |         self.security_token = '' | ||||||
|  |         self.request_id = '' | ||||||
|  |         self.bucket='' | ||||||
|  |         self.endpoint='' | ||||||
|  | 
 | ||||||
|  | class AliYunOss(object): | ||||||
|  | 
 | ||||||
|  |     def __init__(self,access_key,secret_key,bucket_name,endpoint,sts_role_arn): | ||||||
|  |         self.access_key_id = access_key or os.getenv('OSS_TEST_STS_ID', 'LTAI4FeTyvz74CHKCbYuTDGc') | ||||||
|  |         self.access_key_secret = secret_key or os.getenv('OSS_TEST_STS_KEY', 'uineAEE7tp1d1w4Mv0yLqxvV0IVNTy') | ||||||
|  |         self.bucket_name = bucket_name or os.getenv('OSS_TEST_BUCKET', 'fir-storage') | ||||||
|  |         self.endpoint = endpoint or os.getenv('OSS_TEST_ENDPOINT', 'oss-cn-beijing.aliyuncs.com') | ||||||
|  |         self.sts_role_arn = sts_role_arn or os.getenv('OSS_TEST_STS_ARN', 'acs:ram::1622120977387033:role/appstorage') | ||||||
|  |         self.region_id = '-'.join( self.endpoint.split('.')[0].split("-")[1:3]) | ||||||
|  |         self.token = StsToken() | ||||||
|  |         self.get_auth_bucket('init', 1800) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def fetch_sts_token(self,name,expires): | ||||||
|  |         """子用户角色扮演获取临时用户的密钥 | ||||||
|  |         :param access_key_id: 子用户的 access key id | ||||||
|  |         :param access_key_secret: 子用户的 access key secret | ||||||
|  |         :param role_arn: STS角色的Arn | ||||||
|  |         :return StsToken: 临时用户密钥 | ||||||
|  |         """ | ||||||
|  |         clt = client.AcsClient(self.access_key_id, self.access_key_secret, self.region_id) | ||||||
|  |         req = AssumeRoleRequest.AssumeRoleRequest() | ||||||
|  | 
 | ||||||
|  |         req.set_accept_format('json') | ||||||
|  |         req.set_RoleArn(self.sts_role_arn) | ||||||
|  |         req.set_RoleSessionName(name[0:31]) | ||||||
|  |         req.set_DurationSeconds(expires) | ||||||
|  | 
 | ||||||
|  |         body = clt.do_action_with_exception(req) | ||||||
|  | 
 | ||||||
|  |         j = json.loads(oss2.to_unicode(body)) | ||||||
|  | 
 | ||||||
|  |         self.token.access_key_id = j['Credentials']['AccessKeyId'] | ||||||
|  |         self.token.access_key_secret = j['Credentials']['AccessKeySecret'] | ||||||
|  |         self.token.security_token = j['Credentials']['SecurityToken'] | ||||||
|  |         self.token.request_id = j['RequestId'] | ||||||
|  |         self.token.expiration = oss2.utils.to_unixtime(j['Credentials']['Expiration'], '%Y-%m-%dT%H:%M:%SZ') | ||||||
|  |         self.token.bucket=self.bucket_name | ||||||
|  |         self.token.endpoint=self.endpoint | ||||||
|  |         return self.token.__dict__ | ||||||
|  | 
 | ||||||
|  |     def get_upload_token(self,name,expires=1800): | ||||||
|  |         return self.fetch_sts_token(name,expires) | ||||||
|  | 
 | ||||||
|  |     def get_auth_bucket(self,name,expires): | ||||||
|  |         self.fetch_sts_token(name,expires) | ||||||
|  |         auth = oss2.StsAuth(self.token.access_key_id, self.token.access_key_secret, self.token.security_token) | ||||||
|  |         self.bucket = oss2.Bucket(auth, self.endpoint,self.bucket_name) | ||||||
|  | 
 | ||||||
|  |     def get_download_url(self,name,expires=1800): | ||||||
|  |         # self.get_auth_bucket(name,expires) | ||||||
|  |         private_url=self.bucket.sign_url('GET',name,expires) | ||||||
|  |         print(private_url) | ||||||
|  |         return private_url | ||||||
|  | 
 | ||||||
|  |     def del_file(self,name): | ||||||
|  |         # self.fetch_sts_token(name,expires=300) | ||||||
|  |         # auth = oss2.StsAuth(self.token.access_key_id, self.token.access_key_secret, self.token.security_token) | ||||||
|  |         # bucket = oss2.Bucket(auth, self.endpoint,self.bucket_name) | ||||||
|  |         return self.bucket.delete_object(name) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @ -0,0 +1,34 @@ | |||||||
|  | #!/usr/bin/env python | ||||||
|  | # -*- coding:utf-8 -*- | ||||||
|  | # project: 3月  | ||||||
|  | # author: liuyu | ||||||
|  | # date: 2020/3/18 | ||||||
|  | ''' | ||||||
|  | 本地存储api | ||||||
|  | ''' | ||||||
|  | from api.utils.TokenManager import DownloadToken | ||||||
|  | from fir_ser import settings | ||||||
|  | import os | ||||||
|  | 
 | ||||||
|  | class LocalStorage(object): | ||||||
|  |     def __init__(self,**kwargs): | ||||||
|  |         self.domain_name = kwargs.get('domain_name') | ||||||
|  | 
 | ||||||
|  |     def get_upload_token(self,name,expires): | ||||||
|  |         dtoken = DownloadToken() | ||||||
|  |         return dtoken.make_token(name,expires) | ||||||
|  | 
 | ||||||
|  |     def get_download_url(self,name,expires=1800): | ||||||
|  |         dtoken = DownloadToken() | ||||||
|  |         base_url = '/'.join([self.domain_name,'download', name]) | ||||||
|  |         download_url = base_url + "?token=" + dtoken.make_token(name,expires) | ||||||
|  |         return download_url | ||||||
|  | 
 | ||||||
|  |     def del_file(self,name): | ||||||
|  |         try: | ||||||
|  |             os.remove(os.path.join(settings.MEDIA_ROOT, name)) | ||||||
|  |             return True | ||||||
|  |         except Exception as e: | ||||||
|  |             print(e) | ||||||
|  |             return False | ||||||
|  | 
 | ||||||
| @ -0,0 +1,60 @@ | |||||||
|  | #!/usr/bin/env python | ||||||
|  | # -*- coding:utf-8 -*- | ||||||
|  | # project: 3月 | ||||||
|  | # author: liuyu | ||||||
|  | # date: 2020/3/23 | ||||||
|  | 
 | ||||||
|  | from api.models import AppStorage, UserInfo | ||||||
|  | from .aliyunApi import AliYunOss | ||||||
|  | from .qiniuApi import QiNiuOss | ||||||
|  | from .localApi import LocalStorage | ||||||
|  | import json | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Storage(object): | ||||||
|  |     def __init__(self, user): | ||||||
|  |         self.storage = self.get_storage(user) | ||||||
|  | 
 | ||||||
|  |     def get_upload_token(self, filename, expires=1800): | ||||||
|  |         if self.storage: | ||||||
|  |             return self.storage.get_upload_token(filename, expires) | ||||||
|  | 
 | ||||||
|  |     def get_download_url(self, filename, expires=1800): | ||||||
|  |         if self.storage: | ||||||
|  |             return self.storage.get_download_url(filename, expires) | ||||||
|  | 
 | ||||||
|  |     def delete_file(self, filename, apptype=None): | ||||||
|  |         if self.storage: | ||||||
|  |             if apptype is not None: | ||||||
|  |                 if apptype == 0: | ||||||
|  |                     filename = filename + '.apk' | ||||||
|  |                 else: | ||||||
|  |                     filename = filename + '.ipa' | ||||||
|  |             return self.storage.del_file(filename) | ||||||
|  | 
 | ||||||
|  |     def get_storage(self, user): | ||||||
|  |         storage_obj = user.storage | ||||||
|  |         if storage_obj: | ||||||
|  |             auth = self.get_storage_auth(storage_obj) | ||||||
|  |             storage_type = storage_obj.storage_type | ||||||
|  |             if storage_type == 1: | ||||||
|  |                 new_storage_obj = QiNiuOss(**auth) | ||||||
|  |             elif storage_type == 2: | ||||||
|  |                 new_storage_obj = AliYunOss(**auth) | ||||||
|  |             else: | ||||||
|  |                 new_storage_obj = LocalStorage(**auth) | ||||||
|  |             return new_storage_obj | ||||||
|  |         return None | ||||||
|  | 
 | ||||||
|  |     def get_storage_auth(self, storage_obj): | ||||||
|  |         auth_dict = { | ||||||
|  |             'access_key': storage_obj.access_key, | ||||||
|  |             'secret_key': storage_obj.secret_key, | ||||||
|  |             'bucket_name': storage_obj.bucket_name, | ||||||
|  |         } | ||||||
|  |         try: | ||||||
|  |             additionalparameters = json.loads(storage_obj.additionalparameters) | ||||||
|  |         except Exception as e: | ||||||
|  |             print(e) | ||||||
|  |             additionalparameters = {} | ||||||
|  |         return {**auth_dict, **additionalparameters} | ||||||
| @ -1,465 +0,0 @@ | |||||||
| from rest_framework.views import APIView |  | ||||||
| from api.utils.response import BaseResponse |  | ||||||
| from api.utils.auth import ExpiringTokenAuthentication |  | ||||||
| from rest_framework.response import Response |  | ||||||
| import json |  | ||||||
| import datetime |  | ||||||
| from django.conf import settings |  | ||||||
| from django_redis import get_redis_connection |  | ||||||
| from api.utils.exceptions import CommonException |  | ||||||
| from django.core.exceptions import ObjectDoesNotExist |  | ||||||
| import redis |  | ||||||
| 
 |  | ||||||
| REDIS_CONN = redis.Redis(decode_responses=True) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class AccountView(APIView): |  | ||||||
|     ''' |  | ||||||
|     结算接口 |  | ||||||
|     ''' |  | ||||||
|     authentication_classes = [ExpiringTokenAuthentication, ] |  | ||||||
| 
 |  | ||||||
|     def get_coupon_list(self, request, course_id=None): |  | ||||||
| 
 |  | ||||||
|         now = datetime.datetime.utcnow() |  | ||||||
| 
 |  | ||||||
|         coupon_record_list = CouponRecord.objects.filter( |  | ||||||
|             account=request.user, |  | ||||||
|             status=0, |  | ||||||
|             coupon__valid_begin_date__lte=now, |  | ||||||
|             coupon__valid_end_date__gt=now, |  | ||||||
|             coupon__content_type_id=14, |  | ||||||
|             coupon__object_id=course_id |  | ||||||
| 
 |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         coupon_list = [] |  | ||||||
| 
 |  | ||||||
|         for coupon_record in coupon_record_list: |  | ||||||
|             coupon_list.append({ |  | ||||||
| 
 |  | ||||||
|                 "pk": coupon_record.pk, |  | ||||||
|                 "name": coupon_record.coupon.name, |  | ||||||
|                 "coupon_type": coupon_record.coupon.get_coupon_type_display(), |  | ||||||
|                 "money_equivalent_value": coupon_record.coupon.money_equivalent_value, |  | ||||||
|                 "off_percent": coupon_record.coupon.off_percent, |  | ||||||
|                 "minimum_consume": coupon_record.coupon.minimum_consume, |  | ||||||
|             }) |  | ||||||
| 
 |  | ||||||
|         return coupon_list |  | ||||||
| 
 |  | ||||||
|     def post(self, request, *args, **kwargs): |  | ||||||
|         # 1 获取数据 |  | ||||||
|         ''' |  | ||||||
|         course_list=[{ |  | ||||||
|                           "course_id":1, |  | ||||||
|                           "price_policy_id":2 |  | ||||||
|                         }, |  | ||||||
| 
 |  | ||||||
|                     ] |  | ||||||
| 
 |  | ||||||
|         :param request: |  | ||||||
|         :param args: |  | ||||||
|         :param kwargs: |  | ||||||
|         :return: |  | ||||||
|         ''' |  | ||||||
|         user = request.user |  | ||||||
|         course_list = request.data |  | ||||||
|         print("course_list",course_list) |  | ||||||
|         print("course_list",type(course_list)) |  | ||||||
|         response = BaseResponse() |  | ||||||
|         try: |  | ||||||
|             # 2 创建数据结构 |  | ||||||
|             # 清空操作 |  | ||||||
|             # 找到所有以account_userid_*,全部清空 |  | ||||||
|             del_list = REDIS_CONN.keys(settings.ACCOUNT_KEY % (user.pk, "*")) |  | ||||||
|             if del_list: |  | ||||||
|                 REDIS_CONN.delete(*del_list) |  | ||||||
| 
 |  | ||||||
|             price_list=[] |  | ||||||
|             for course_dict in course_list: |  | ||||||
| 
 |  | ||||||
|                 course_id=course_dict.get("course_id") |  | ||||||
|                 price_policy_id=course_dict.get("price_policy_id") |  | ||||||
|                 # 校验课程是否存在 |  | ||||||
|                 course_obj=Course.objects.get(pk=course_id) |  | ||||||
|                 # 查找课程关联的价格策略 |  | ||||||
|                 price_policy_list = course_obj.price_policy.all() |  | ||||||
|                 price_policy_dict = {} |  | ||||||
|                 for price_policy in price_policy_list: |  | ||||||
|                     price_policy_dict[price_policy.pk] = { |  | ||||||
|                         "prcie": price_policy.price, |  | ||||||
|                         "valid_period": price_policy.valid_period, |  | ||||||
|                         "valid_period_text": price_policy.get_valid_period_display(), |  | ||||||
|                         "default": price_policy.pk == price_policy_id |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                 if price_policy_id not in price_policy_dict: |  | ||||||
|                     raise CommonException(1001, "价格策略异常!") |  | ||||||
|                 pp=PricePolicy.objects.get(pk=price_policy_id) |  | ||||||
|                 # 将课程信息加入到每一个课程结算字典中 |  | ||||||
|                 account_dict = { |  | ||||||
|                     "id": course_id, |  | ||||||
|                     "name": course_obj.name, |  | ||||||
|                     "course_img": course_obj.course_img, |  | ||||||
|                     "relate_price_policy": price_policy_dict, |  | ||||||
|                     "default_price": pp.price, |  | ||||||
|                     "rebate_price": pp.price, |  | ||||||
|                     "default_price_period": pp.valid_period, |  | ||||||
|                     "default_price_policy_id": pp.pk |  | ||||||
|                 } |  | ||||||
|                 # 课程价格加入到价格列表 |  | ||||||
|                 price_list.append(float(pp.price)) |  | ||||||
| 
 |  | ||||||
|                 # 查询当前用户拥有未使用的,在有效期的且与当前课程相关的优惠券 |  | ||||||
|                 account_dict["coupon_list"] = self.get_coupon_list(request, course_id) |  | ||||||
| 
 |  | ||||||
|                 # 存储结算信息 |  | ||||||
|                 account_key = settings.ACCOUNT_KEY % (user.pk, course_id) |  | ||||||
|                 REDIS_CONN.set(account_key, json.dumps(account_dict)) |  | ||||||
| 
 |  | ||||||
|             # 获取通用优惠券,加入redis中 |  | ||||||
| 
 |  | ||||||
|             REDIS_CONN.set("global_coupon_%s" % user.pk, json.dumps(self.get_coupon_list(request))) |  | ||||||
|             REDIS_CONN.set("total_price",sum(price_list)) |  | ||||||
| 
 |  | ||||||
|         except ObjectDoesNotExist as e: |  | ||||||
|             response.code = 1001 |  | ||||||
|             response.error = "课程不存在!" |  | ||||||
|         except CommonException as e: |  | ||||||
|             response.code = e.code |  | ||||||
|             response.error = e.error |  | ||||||
| 
 |  | ||||||
|         # except Exception as e: |  | ||||||
|         #     response.code = 500 |  | ||||||
|         #     response.error = str(e) |  | ||||||
| 
 |  | ||||||
|         return Response(response.dict) |  | ||||||
| 
 |  | ||||||
|     def get(self, request, *args, **kwargs): |  | ||||||
| 
 |  | ||||||
|         res = BaseResponse() |  | ||||||
|         try: |  | ||||||
|             # 1 取到user_id |  | ||||||
|             user_id = request.user.id |  | ||||||
|             # 2 拼接购物车的key |  | ||||||
|             account_key = settings.ACCOUNT_KEY % (user_id, "*") |  | ||||||
|             # shopping_car_1_* |  | ||||||
|             # shopping_car_1_asdgnlaksdj |  | ||||||
|             # 3 去redis读取该用户的所有加入购物车的课程 |  | ||||||
|             # 3.1 先去模糊匹配出所有符合要求的key |  | ||||||
|             all_keys = REDIS_CONN.scan_iter(account_key) |  | ||||||
| 
 |  | ||||||
|             # 3.2 循环所有的keys 得到每个可以 |  | ||||||
|             account_course_list = [] |  | ||||||
|             for key in all_keys: |  | ||||||
|                 account_course = json.loads(REDIS_CONN.get(key)) |  | ||||||
|                 account_course_list.append(account_course) |  | ||||||
| 
 |  | ||||||
|             global_coupons = json.loads(REDIS_CONN.get("global_coupon_%s" % request.user.pk)) |  | ||||||
|             total_price = REDIS_CONN.get("total_price") |  | ||||||
|             res.data = { |  | ||||||
|                 "account_course_list": account_course_list, |  | ||||||
|                 "total": len(account_course_list), |  | ||||||
|                 "global_coupons": global_coupons, |  | ||||||
|                 "total_price": total_price |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         except Exception as e: |  | ||||||
|             res.code = 1033 |  | ||||||
|             res.error = "获取购物车失败" |  | ||||||
| 
 |  | ||||||
|         return Response(res.dict) |  | ||||||
| 
 |  | ||||||
|     def cal_coupon_price(self,price,coupon_info): |  | ||||||
| 
 |  | ||||||
|         print("coupon_info",coupon_info) |  | ||||||
|         coupon_type=coupon_info["coupon_type"] |  | ||||||
|         money_equivalent_value=coupon_info.get("money_equivalent_value") |  | ||||||
|         off_percent=coupon_info.get("off_percent") |  | ||||||
|         minimum_consume=coupon_info.get("minimum_consume") |  | ||||||
|         rebate_price=0 |  | ||||||
|         if coupon_type == "立减券": # 立减券 |  | ||||||
|             rebate_price=price-money_equivalent_value |  | ||||||
|             if rebate_price <= 0: |  | ||||||
|                 rebate_price=0 |  | ||||||
|         elif coupon_type == "满减券": # 满减券 |  | ||||||
|              if minimum_consume > price: |  | ||||||
|                  raise CommonException(3000,"优惠券未达到最低消费") |  | ||||||
|              else: |  | ||||||
|                  rebate_price=price-money_equivalent_value |  | ||||||
|         elif coupon_type == "折扣券": |  | ||||||
|             rebate_price=price*off_percent/100 |  | ||||||
| 
 |  | ||||||
|         return rebate_price |  | ||||||
| 
 |  | ||||||
|     def put(self,request, *args, **kwargs): |  | ||||||
|         ''' |  | ||||||
|         choose_coupons: |  | ||||||
|             { |  | ||||||
|             choose_coupons={"1":2,"2":3,"global_coupon_id":5} |  | ||||||
|             is_beli:true |  | ||||||
|             } |  | ||||||
|         ''' |  | ||||||
|         res=BaseResponse() |  | ||||||
|         # try: |  | ||||||
| 
 |  | ||||||
|         # 1 获取数据 |  | ||||||
|         choose_coupons=request.data.get("choose_coupons") |  | ||||||
|         is_beli=request.data.get("is_beli") |  | ||||||
|         user_pk=request.user.pk |  | ||||||
| 
 |  | ||||||
|         # 2 获取结算课程列表 |  | ||||||
|         cal_price={} |  | ||||||
|         data=self.get(request).data.get("data") |  | ||||||
|         account_course_list=data.get("account_course_list") |  | ||||||
|         print("account_course_list",account_course_list) |  | ||||||
|         ''' |  | ||||||
|            account_course_list=[{ |  | ||||||
|                 'id': 4, |  | ||||||
|                 'coupon_list': [{ |  | ||||||
|                     'off_percent': None, |  | ||||||
|                     'pk': 4, |  | ||||||
|                     'money_equivalent_value': 300.0, |  | ||||||
|                     'coupon_type': '立减券', |  | ||||||
|                     'minimum_consume': 0, |  | ||||||
|                     'name': '51劳动节' |  | ||||||
|                 }], |  | ||||||
|                 'course_img': 'https://luffycity.com/static/frontend/course/12/Linux5周入门_1509589530.6144893.png', |  | ||||||
|                 'default_price': 1500.0, |  | ||||||
|                 'default_price_period': 60, |  | ||||||
|                 'relate_price_policy': { |  | ||||||
|                     '5': { |  | ||||||
|                         'valid_period_text': '2个月', |  | ||||||
|                         'default': True, |  | ||||||
|                         'valid_period': 60, |  | ||||||
|                         'prcie': 1500.0 |  | ||||||
|                     }, |  | ||||||
|                     '4': { |  | ||||||
|                         'valid_period_text': '1个月', |  | ||||||
|                         'default': False, |  | ||||||
|                         'valid_period': 30, |  | ||||||
|                         'prcie': 1000.0 |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 'default_price_policy_id': 5, |  | ||||||
|                 'name': 'Linux系统基础5周入门精讲' |  | ||||||
|             }, { |  | ||||||
|                 'id': 2, |  | ||||||
|                 'coupon_list': [{ |  | ||||||
|                     'off_percent': 80, |  | ||||||
|                     'pk': 3, |  | ||||||
|                     'money_equivalent_value': 0.0, |  | ||||||
|                     'coupon_type': '折扣券', |  | ||||||
|                     'minimum_consume': 0, |  | ||||||
|                     'name': '清明节活动' |  | ||||||
|                 }], |  | ||||||
|                 'course_img': 'https://luffycity.com/static/frontend/course/3/Django框架学习_1509095212.759272.png', |  | ||||||
|                 'default_price': 300.0, |  | ||||||
|                 'default_price_period': 30, |  | ||||||
|                 'relate_price_policy': { |  | ||||||
|                     '3': { |  | ||||||
|                         'valid_period_text': '1个月', |  | ||||||
|                         'default': True, |  | ||||||
|                         'valid_period': 30, |  | ||||||
|                         'prcie': 300.0 |  | ||||||
|                     }, |  | ||||||
|                     '1': { |  | ||||||
|                         'valid_period_text': '1周', |  | ||||||
|                         'default': False, |  | ||||||
|                         'valid_period': 7, |  | ||||||
|                         'prcie': 100.0 |  | ||||||
|                     }, |  | ||||||
|                     '2': { |  | ||||||
|                         'valid_period_text': '2周', |  | ||||||
|                         'default': False, |  | ||||||
|                         'valid_period': 14, |  | ||||||
|                         'prcie': 200.0 |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 'default_price_policy_id': 3, |  | ||||||
|                 'name': 'Django框架学习' |  | ||||||
|             }] |  | ||||||
|         ''' |  | ||||||
|         account_courses_info={} |  | ||||||
|         for account_course in account_course_list: |  | ||||||
|             temp={ |  | ||||||
|                 "coupon":{}, |  | ||||||
|                 "default_price":account_course["default_price"] |  | ||||||
|             } |  | ||||||
|             account_courses_info[account_course["id"]]=temp |  | ||||||
| 
 |  | ||||||
|             for item in account_course["coupon_list"]: |  | ||||||
| 
 |  | ||||||
|                 print("choose_coupons",choose_coupons) # {'4': 4} |  | ||||||
|                 print(str(account_course["id"])) |  | ||||||
| 
 |  | ||||||
|                 coupon_id=choose_coupons.get(str(account_course["id"])) |  | ||||||
|                 if coupon_id == item["pk"]: |  | ||||||
|                     temp["coupon"]=item |  | ||||||
| 
 |  | ||||||
|         print("account_course_info",account_courses_info) |  | ||||||
|         price_list=[] |  | ||||||
|         total_price=0 |  | ||||||
|         ''' |  | ||||||
|            { |  | ||||||
|                 2: { |  | ||||||
|                     'coupon': { |  | ||||||
|                         'money_equivalent_value': 0.0, |  | ||||||
|                         'name': '清明节活动', |  | ||||||
|                         'pk': 3, |  | ||||||
|                         'off_percent': 80, |  | ||||||
|                         'coupon_type': '折扣券', |  | ||||||
|                         'minimum_consume': 0 |  | ||||||
|                     }, |  | ||||||
|                     'default_price': 200.0 |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         ''' |  | ||||||
|         for key,val in account_courses_info.items(): |  | ||||||
|             if not val.get("coupon"): |  | ||||||
|                 price_list.append(val["default_price"]) |  | ||||||
|                 cal_price[key]=val["default_price"] |  | ||||||
|             else: |  | ||||||
|                 coupon_info=val.get("coupon") |  | ||||||
|                 default_price=val["default_price"] |  | ||||||
|                 rebate_price=self.cal_coupon_price(default_price,coupon_info) |  | ||||||
|                 price_list.append(rebate_price) |  | ||||||
|                 cal_price[key]=rebate_price |  | ||||||
| 
 |  | ||||||
|         print("课程优惠券后价格列表price_list",price_list) |  | ||||||
|         total_price=sum(price_list) |  | ||||||
|         # 3 计算通用优惠券的价格 |  | ||||||
|         global_coupon_id=choose_coupons.get("global_coupon_id") |  | ||||||
|         if global_coupon_id: |  | ||||||
| 
 |  | ||||||
|             global_coupons=data.get("global_coupons") |  | ||||||
|             print("global_coupons",global_coupons) |  | ||||||
|             global_coupon_dict={} |  | ||||||
|             for item in global_coupons: |  | ||||||
|                 global_coupon_dict[item["pk"]]=item |  | ||||||
|             total_price=self.cal_coupon_price(total_price,global_coupon_dict[global_coupon_id]) |  | ||||||
|             print("通用优惠券",global_coupon_dict[global_coupon_id]["coupon_type"]) |  | ||||||
|             print("计算后total_price=",total_price) |  | ||||||
| 
 |  | ||||||
|         # 计算贝里 |  | ||||||
|         if json.loads(is_beli): |  | ||||||
|             print("request.user.beli",request.user.beli) |  | ||||||
|             total_price=total_price-request.user.beli/10 |  | ||||||
|             if total_price<0: |  | ||||||
|                 total_price=0 |  | ||||||
|             print("贝里数计算后",total_price) |  | ||||||
| 
 |  | ||||||
|         cal_price["total_price"]=total_price |  | ||||||
|         res.data=cal_price |  | ||||||
| 
 |  | ||||||
|         # except Exception as e: |  | ||||||
|         #     res.code=500 |  | ||||||
|         #     res.msg="结算错误!"+str(e) |  | ||||||
| 
 |  | ||||||
|         return Response(res.dict) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ''' |  | ||||||
| 1 结算中心post添加接口数据结构: |  | ||||||
|         { |  | ||||||
|         "course_img": "https://luffycity.com/static/frontend/course/3/Django框架学习_1509095212.759272.png", |  | ||||||
|         "coupon_list": [{ |  | ||||||
|             "name": "清明节活动", |  | ||||||
|             "minimum_consume": 0, |  | ||||||
|             "money_equivalent_value": 0.0, |  | ||||||
|             "off_percent": 80, |  | ||||||
|             "pk": 3, |  | ||||||
|             "coupon_type": "折扣券" |  | ||||||
|         }], |  | ||||||
|         "relate_price_policy": { |  | ||||||
|             "1": { |  | ||||||
|                 "valid_period": 7, |  | ||||||
|                 "valid_period_text": "1周", |  | ||||||
|                 "prcie": 100.0, |  | ||||||
|                 "default": true |  | ||||||
|             }, |  | ||||||
|             "2": { |  | ||||||
|                 "valid_period": 14, |  | ||||||
|                 "valid_period_text": "2周", |  | ||||||
|                 "prcie": 200.0, |  | ||||||
|                 "default": false |  | ||||||
|             }, |  | ||||||
|             "3": { |  | ||||||
|                 "valid_period": 30, |  | ||||||
|                 "valid_period_text": "1个月", |  | ||||||
|                 "prcie": 300.0, |  | ||||||
|                 "default": false |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         "name": "Django框架学习", |  | ||||||
|         "default_price": 100.0, |  | ||||||
|         "id": 2, |  | ||||||
|         "default_price_period": 7, |  | ||||||
|         "default_price_policy_id": 1 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 2 结算中心get查询接口: |  | ||||||
| 
 |  | ||||||
| { |  | ||||||
|     "data": { |  | ||||||
|         "total": 1, |  | ||||||
|         "global_coupons": [ |  | ||||||
|             { |  | ||||||
|                 "name": "国庆节活动通用券", |  | ||||||
|                 "coupon_type": "满减券", |  | ||||||
|                 "minimum_consume": 100, |  | ||||||
|                 "money_equivalent_value": 50, |  | ||||||
|                 "off_percent": null, |  | ||||||
|                 "pk": 2 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "total_price": "100.0", |  | ||||||
|         "account_course_list": [ |  | ||||||
|             { |  | ||||||
|                 "course_img": "https://luffycity.com/static/frontend/course/3/Django框架学习_1509095212.759272.png", |  | ||||||
|                 "name": "Django框架学习", |  | ||||||
|                 "relate_price_policy": { |  | ||||||
|                     "1": { |  | ||||||
|                         "valid_period": 7, |  | ||||||
|                         "prcie": 100, |  | ||||||
|                         "valid_period_text": "1周", |  | ||||||
|                         "default": true |  | ||||||
|                     }, |  | ||||||
|                     "2": { |  | ||||||
|                         "valid_period": 14, |  | ||||||
|                         "prcie": 200, |  | ||||||
|                         "valid_period_text": "2周", |  | ||||||
|                         "default": false |  | ||||||
|                     }, |  | ||||||
|                     "3": { |  | ||||||
|                         "valid_period": 30, |  | ||||||
|                         "prcie": 300, |  | ||||||
|                         "valid_period_text": "1个月", |  | ||||||
|                         "default": false |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 "coupon_list": [ |  | ||||||
|                     { |  | ||||||
|                         "name": "清明节活动", |  | ||||||
|                         "coupon_type": "折扣券", |  | ||||||
|                         "minimum_consume": 0, |  | ||||||
|                         "money_equivalent_value": 0, |  | ||||||
|                         "off_percent": 80, |  | ||||||
|                         "pk": 3 |  | ||||||
|                     } |  | ||||||
|                 ], |  | ||||||
|                 "default_price": 100, |  | ||||||
|                 "id": 2, |  | ||||||
|                 "default_price_period": 7, |  | ||||||
|                 "default_price_policy_id": 1 |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     "code": 1000, |  | ||||||
|     "msg": "" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ''' |  | ||||||
| @ -1,41 +0,0 @@ | |||||||
| from api import models |  | ||||||
| from api.utils import serializer |  | ||||||
| 
 |  | ||||||
| from rest_framework.response import Response |  | ||||||
| from rest_framework.viewsets import ModelViewSet |  | ||||||
| from api.utils.auth import ExpiringTokenAuthentication |  | ||||||
| from api.utils.filter import CourseFilter |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class CourseView(ModelViewSet): |  | ||||||
|     # authentication_classes = [ExpiringTokenAuthentication,] |  | ||||||
|     queryset = models.Course.objects.all() |  | ||||||
|     serializer_class = serializer.CourseSerializer |  | ||||||
|     filter_backends = [CourseFilter,] |  | ||||||
|     def list(self, request, *args, **kwargs): |  | ||||||
|         queryset = self.filter_queryset(self.get_queryset()) |  | ||||||
| 
 |  | ||||||
|         page = self.paginate_queryset(queryset) |  | ||||||
|         if page is not None: |  | ||||||
|             serializer = self.get_serializer(page, many=True) |  | ||||||
|             return self.get_paginated_response(serializer.data) |  | ||||||
| 
 |  | ||||||
|         serializer = self.get_serializer(queryset, many=True) |  | ||||||
| 
 |  | ||||||
|         return Response({"code": 0, "data": serializer.data}) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class CourseDetailView(ModelViewSet): |  | ||||||
|     queryset = models.CourseDetail.objects.all() |  | ||||||
|     serializer_class = serializer.CourseDetailSerializer |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class CourseCategoryView(ModelViewSet): |  | ||||||
|     queryset = models.CourseCategory.objects.all() |  | ||||||
|     serializer_class = serializer.CourseCategorySerializer |  | ||||||
| 
 |  | ||||||
|     def list(self, request, *args, **kwargs): |  | ||||||
|         queryset = self.filter_queryset(self.get_queryset()) |  | ||||||
|         serializer = self.get_serializer(queryset, many=True) |  | ||||||
|         return Response({"error_no":0,"data":serializer.data}) |  | ||||||
| @ -1,42 +0,0 @@ | |||||||
| from rest_framework.views import APIView |  | ||||||
| from rest_framework.response import Response |  | ||||||
| from api.utils.auth import ExpiringTokenAuthentication |  | ||||||
| from django.core.exceptions import ObjectDoesNotExist |  | ||||||
| from api.utils.response import BaseResponse |  | ||||||
| from api.models import Course, CouponRecord, Coupon, PricePolicy, Order, OrderDetail |  | ||||||
| from api.utils.exceptions import CommonException |  | ||||||
| import datetime |  | ||||||
| import uuid |  | ||||||
| import time |  | ||||||
| import json |  | ||||||
| 
 |  | ||||||
| from api.models import Order, OrderDetail |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class OrderView(APIView): |  | ||||||
|     authentication_classes = [ExpiringTokenAuthentication] |  | ||||||
| 
 |  | ||||||
|     def get(self, request, *args, **kwargs): |  | ||||||
|         res = BaseResponse() |  | ||||||
|         order_list = Order.objects.filter(account=request.user).order_by("-date") |  | ||||||
|         data = [] |  | ||||||
|         for order in order_list: |  | ||||||
|             data.append({ |  | ||||||
|                 "order_number": order.order_number, |  | ||||||
|                 "date": order.date.strftime("%Y-%m-%d %H:%M:%S"), |  | ||||||
|                 "status": order.get_status_display(), |  | ||||||
|                 "actual_amount": order.actual_amount, |  | ||||||
|                 "orderdetail_list": [{ |  | ||||||
|                                          "original_price": obj.original_price, |  | ||||||
|                                          "price": obj.price, |  | ||||||
|                                          "course_name": obj.content_object.name, |  | ||||||
|                                          "valid_period_display": obj.valid_period_display, |  | ||||||
|                                      } for obj in order.orderdetail_set.all()] |  | ||||||
|             }) |  | ||||||
| 
 |  | ||||||
|         print("data", data) |  | ||||||
|         res.data = data |  | ||||||
| 
 |  | ||||||
|         return Response(res.dict) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @ -1,263 +0,0 @@ | |||||||
| from rest_framework.views import APIView |  | ||||||
| from rest_framework.response import Response |  | ||||||
| from django.http import JsonResponse |  | ||||||
| from api.utils.auth import ExpiringTokenAuthentication |  | ||||||
| from django.core.exceptions import ObjectDoesNotExist |  | ||||||
| from api.utils.response import BaseResponse |  | ||||||
| from api.models import Course, CouponRecord, Coupon, PricePolicy,Order,OrderDetail |  | ||||||
| from api.utils.exceptions import CommonException |  | ||||||
| 
 |  | ||||||
| import random,datetime,time,os,sys |  | ||||||
| from api.utils.ali.api import ali_api |  | ||||||
| 
 |  | ||||||
| import redis |  | ||||||
| from django.conf import settings |  | ||||||
| 
 |  | ||||||
| REDIS_CONN = redis.Redis(decode_responses=True) |  | ||||||
| 
 |  | ||||||
| class PaymentView(APIView): |  | ||||||
|     ''' |  | ||||||
|     模拟请求数据格式: |  | ||||||
| 
 |  | ||||||
|       { |  | ||||||
|       is_beli:true, |  | ||||||
|       course_list=[ |  | ||||||
|                   {  course_id:1 |  | ||||||
|                    default_price_policy_id:1, |  | ||||||
|                    coupon_record_id:2 |  | ||||||
|                    }, |  | ||||||
|                   { course_id:2 |  | ||||||
|                    default_price_policy_id:4, |  | ||||||
|                    coupon_record_id:6 |  | ||||||
|                    } |  | ||||||
|                ], |  | ||||||
|        global_coupon_id:3, |  | ||||||
|        pay_money:298 |  | ||||||
| 
 |  | ||||||
|        } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|      状态码: |  | ||||||
|          1000:  成功 |  | ||||||
|          1001:  课程不存在 |  | ||||||
|          1002:  价格策略不合法 |  | ||||||
|          1003:  加入购物车失败 |  | ||||||
|          1004:  获取购物车失败 |  | ||||||
|          1005:  贝里数有问题 |  | ||||||
|          1006:  优惠券异常 |  | ||||||
|          1007:  优惠券未达到最低消费 |  | ||||||
|          1008:  支付总价格异常 |  | ||||||
| 
 |  | ||||||
|     ''' |  | ||||||
|     authentication_classes = [ExpiringTokenAuthentication] |  | ||||||
| 
 |  | ||||||
|     def get_pay_url(self,request,order_number,final_price): |  | ||||||
| 
 |  | ||||||
|         # 4 调用支付宝支付接口(二维码页面) |  | ||||||
| 
 |  | ||||||
|         if request.META["HTTP_USER_AGENT"]: |  | ||||||
| 
 |  | ||||||
|             pay_api = ali_api.pay.pc |  | ||||||
| 
 |  | ||||||
|         elif request == "APP": |  | ||||||
|             pay_api = ali_api.pay.app |  | ||||||
| 
 |  | ||||||
|         else: |  | ||||||
|             pay_api = ali_api.pay.wap |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         pay_url = pay_api.direct( |  | ||||||
|             subject="路飞学城",  # 商品简单描述 |  | ||||||
|             out_trade_no=order_number,  # 商户订单号 |  | ||||||
|             total_amount=final_price,  # 交易金额(单位: 元 保留俩位小数) |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         print("pay_url",pay_url) |  | ||||||
|         return pay_url |  | ||||||
| 
 |  | ||||||
|     def get_order_num(self): |  | ||||||
|         now=datetime.datetime.now() |  | ||||||
|         orderType="1" |  | ||||||
|         dateStr4yyyyMMddHHmmss="{0}{1}{2}".format(now.year,now.month,now.day) |  | ||||||
|         rand=str(random.randint(1000,9999)) |  | ||||||
| 
 |  | ||||||
|         s=orderType+dateStr4yyyyMMddHHmmss+rand |  | ||||||
| 
 |  | ||||||
|         return s |  | ||||||
| 
 |  | ||||||
|     def post(self, request, *args, **kwargs): |  | ||||||
|         print(request.data) |  | ||||||
|         response = BaseResponse() |  | ||||||
|         # 1 获取数据 |  | ||||||
|         user_id = request.user.pk |  | ||||||
|         global_coupon_id = request.data.get("global_coupon_id") |  | ||||||
|         pay_money = request.data.get("pay_money") |  | ||||||
|         course_list = request.data.get("course_list") |  | ||||||
|         is_beli = request.data.get("is_beli") |  | ||||||
|         now = datetime.datetime.now() |  | ||||||
|         try: |  | ||||||
|             # 2 校验数据 |  | ||||||
|             # 2.2 校验课程 |  | ||||||
|             course_price_list = [] |  | ||||||
|             for course_dict in course_list: |  | ||||||
|                 # 2.2.1 校验课程id |  | ||||||
| 
 |  | ||||||
|                 course_id = course_dict.get("course_id") |  | ||||||
|                 print("course_id",course_id) |  | ||||||
|                 course_obj = Course.objects.get(pk=course_id) |  | ||||||
|                 # 2.2.2 价格策略id |  | ||||||
|                 if course_dict.get("default_price_policy_id") not in [obj.pk for obj in course_obj.price_policy.all()]: |  | ||||||
|                     raise CommonException("价格策略异常!", 1002) |  | ||||||
|                 # 2.2.3 课程优惠券id |  | ||||||
| 
 |  | ||||||
|                 price_policy_obj = PricePolicy.objects.get(pk=course_dict.get("default_price_policy_id")) |  | ||||||
|                 course_dict["original_price"]=price_policy_obj.price |  | ||||||
|                 course_dict["valid_period_display"]=price_policy_obj.get_valid_period_display() |  | ||||||
|                 course_dict["valid_period"]=price_policy_obj.valid_period |  | ||||||
|                 coupon_record_id = course_dict.get("coupon_record_id") |  | ||||||
|                 if coupon_record_id: |  | ||||||
| 
 |  | ||||||
|                     coupon_record_list = CouponRecord.objects.filter(account=request.user, |  | ||||||
|                                                                      status=0, |  | ||||||
|                                                                      coupon__valid_begin_date__lt=now, |  | ||||||
|                                                                      coupon__valid_end_date__gt=now, |  | ||||||
|                                                                      coupon__content_type_id=14, |  | ||||||
|                                                                      coupon__object_id=course_id |  | ||||||
|                                                                      ) |  | ||||||
|                     print("coupon_record_id",coupon_record_id) |  | ||||||
| 
 |  | ||||||
|                     if coupon_record_id and coupon_record_id not in [obj.pk for obj in coupon_record_list]: |  | ||||||
|                         raise CommonException("课程优惠券异常!", 1006) |  | ||||||
| 
 |  | ||||||
|                     # 计算循环课程的课程优惠券优惠后的价格 |  | ||||||
|                     coupon_record_obj = CouponRecord.objects.get(pk=coupon_record_id) |  | ||||||
|                     rebate_price = self.cal_coupon_price(price_policy_obj.price, coupon_record_obj) |  | ||||||
|                     course_price_list.append(rebate_price) |  | ||||||
|                     course_dict["rebate_price"]=rebate_price |  | ||||||
|                 else: |  | ||||||
|                     course_price_list.append(price_policy_obj.price) |  | ||||||
| 
 |  | ||||||
|             # 2.3 校验通用优惠券id |  | ||||||
|             global_coupon_record_list = CouponRecord.objects.filter(account=request.user, |  | ||||||
|                                                                     status=0, |  | ||||||
|                                                                     coupon__valid_begin_date__lt=now, |  | ||||||
|                                                                     coupon__valid_end_date__gt=now, |  | ||||||
|                                                                     coupon__content_type_id=14, |  | ||||||
|                                                                     coupon__object_id=None |  | ||||||
|                                                                     ) |  | ||||||
|             if global_coupon_id and global_coupon_id not in [obj.pk for obj in global_coupon_record_list]: |  | ||||||
|                 raise CommonException("通用优惠券异常", 1006) |  | ||||||
| 
 |  | ||||||
|             if global_coupon_id: |  | ||||||
|                 global_coupon_record_obj = CouponRecord.objects.get(pk=global_coupon_id) |  | ||||||
|                 final_price = self.cal_coupon_price(sum(course_price_list), global_coupon_record_obj) |  | ||||||
|             else: |  | ||||||
|                 final_price=sum(course_price_list) |  | ||||||
|             # 2.4 计算实际支付价格与money做校验 |  | ||||||
|             cost_beli_num=0 |  | ||||||
|             if is_beli: |  | ||||||
|                 final_price = final_price - request.user.beli / 10 |  | ||||||
|                 cost_beli_num=request.user.beli |  | ||||||
|                 if final_price < 0: |  | ||||||
|                     final_price = 0 |  | ||||||
|                     cost_beli_num=final_price*10 |  | ||||||
|                 print(final_price) |  | ||||||
| 
 |  | ||||||
|             if final_price != float(pay_money): |  | ||||||
|                 raise CommonException(1008, "支付总价格异常!") |  | ||||||
| 
 |  | ||||||
|             # 3 生成订单记录 |  | ||||||
|             # Order记录 |  | ||||||
|             # orderDetail |  | ||||||
|             # orderDetail |  | ||||||
|             # orderDetail |  | ||||||
|             order_number=self.get_order_num() |  | ||||||
|             print("order_number",order_number) |  | ||||||
|             order_obj=Order.objects.create( |  | ||||||
|                 payment_type=1, |  | ||||||
|                 order_number=order_number, |  | ||||||
|                 account=request.user, |  | ||||||
|                 status=1, |  | ||||||
|                 order_type=1, |  | ||||||
|                 actual_amount=pay_money, |  | ||||||
|             ) |  | ||||||
|             print("course_list",course_list) |  | ||||||
| 
 |  | ||||||
|             for course_item in course_list: |  | ||||||
|                 OrderDetail.objects.create( |  | ||||||
|                     order=order_obj, |  | ||||||
|                     content_type_id=14, |  | ||||||
|                     object_id=course_item.get("course_id"), |  | ||||||
|                     original_price=course_item.get("original_price"), |  | ||||||
|                     price=course_item.get("rebate_price") or course_item.get("original_price"), |  | ||||||
|                     valid_period=course_item.get("valid_period"), |  | ||||||
|                     valid_period_display=course_item.get("valid_period_display"), |  | ||||||
|                 ) |  | ||||||
| 
 |  | ||||||
|             request.user.beli=request.user.beli-cost_beli_num |  | ||||||
|             request.user.save() |  | ||||||
|             REDIS_CONN.set(order_number+"|"+str(cost_beli_num),"",20) |  | ||||||
|             account_key=settings.ACCOUNT_KEY%(user_id,"*") |  | ||||||
|             REDIS_CONN.delete(*REDIS_CONN.keys(account_key)) |  | ||||||
|             ''' |  | ||||||
|                [ |  | ||||||
|                   {  course_id:1 |  | ||||||
|                      default_price_policy_id:1, |  | ||||||
|                      coupon_record_id:2 |  | ||||||
|                    }, |  | ||||||
|                   { |  | ||||||
|                      course_id:2 |  | ||||||
|                      default_price_policy_id:4, |  | ||||||
|                      coupon_record_id:6 |  | ||||||
|                    } |  | ||||||
|                ] |  | ||||||
|             ''' |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|             response.data = self.get_pay_url(request,order_number,final_price) |  | ||||||
| 
 |  | ||||||
|         except ObjectDoesNotExist as e: |  | ||||||
|             response.code = 1001 |  | ||||||
|             response.msg = "课程不存在!" |  | ||||||
|         except CommonException as e: |  | ||||||
|             response.code = e.code |  | ||||||
|             response.msg = e.error |  | ||||||
|         except Exception as e: |  | ||||||
|             response.code = 500 |  | ||||||
|             response.msg = str(e) |  | ||||||
| 
 |  | ||||||
|         return Response(response.dict) |  | ||||||
| 
 |  | ||||||
|     def cal_coupon_price(self, price, coupon_record_obj): |  | ||||||
|         coupon_type = coupon_record_obj.coupon.coupon_type |  | ||||||
|         money_equivalent_value = coupon_record_obj.coupon.money_equivalent_value |  | ||||||
|         off_percent = coupon_record_obj.coupon.off_percent |  | ||||||
|         minimum_consume = coupon_record_obj.coupon.minimum_consume |  | ||||||
|         rebate_price = 0 |  | ||||||
|         if coupon_type == 0:  # 立减券 |  | ||||||
|             rebate_price = price - money_equivalent_value |  | ||||||
|             if rebate_price <= 0: |  | ||||||
|                 rebate_price = 0 |  | ||||||
|         elif coupon_type == 1:  # 满减券 |  | ||||||
|             if minimum_consume > price: |  | ||||||
|                 raise CommonException(1007, "优惠券未达到最低消费") |  | ||||||
|             else: |  | ||||||
|                 rebate_price = price - money_equivalent_value |  | ||||||
|         elif coupon_type == 2: |  | ||||||
|             rebate_price = price * off_percent / 100 |  | ||||||
| 
 |  | ||||||
|         return rebate_price |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def get_pay_url(request): |  | ||||||
|     print('--->',request.GET.get("order_number")) |  | ||||||
|     pay_url = ali_api.pay.pc.direct( |  | ||||||
|         subject="python全栈课程",  # 商品简单描述 |  | ||||||
|         out_trade_no=request.GET.get("order_number"),  # 商户订单号 |  | ||||||
|         total_amount=request.GET.get("final_price"),  # 交易金额(单位: 元 保留俩位小数) |  | ||||||
|     ) |  | ||||||
|     print("pay_url",pay_url) |  | ||||||
| 
 |  | ||||||
|     return JsonResponse({"pay_url":pay_url}) |  | ||||||
| 
 |  | ||||||
| @ -1,258 +0,0 @@ | |||||||
| om rest_framework.views import APIView |  | ||||||
| from rest_framework.viewsets import ViewSetMixin, ModelViewSet |  | ||||||
| 
 |  | ||||||
| from api.utils.response import BaseResponse |  | ||||||
| 
 |  | ||||||
| from rest_framework.response import Response |  | ||||||
| 
 |  | ||||||
| from api.models import Course, CourseDetail, PricePolicy |  | ||||||
| from django.core.exceptions import ObjectDoesNotExist |  | ||||||
| from api.utils.exceptions import CommonException |  | ||||||
| from django_redis import get_redis_connection |  | ||||||
| from django.conf import settings |  | ||||||
| from api.utils.auth import ExpiringTokenAuthentication |  | ||||||
| 
 |  | ||||||
| import json |  | ||||||
| from api.utils.permission import LoginUserPermission |  | ||||||
| import redis |  | ||||||
| 
 |  | ||||||
| REDIS_CONN = redis.Redis(decode_responses=True) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class ShoppingCarView(APIView): |  | ||||||
|     """ |  | ||||||
|     1030 加入购物车失败 |  | ||||||
|     """ |  | ||||||
|     authentication_classes = [ExpiringTokenAuthentication, ] |  | ||||||
| 
 |  | ||||||
|     def post(self, request): |  | ||||||
|         res = BaseResponse() |  | ||||||
|         try: |  | ||||||
|             # 1 获取前端传过来的course_id 以及price_policy_id user_id |  | ||||||
|             course_id = request.data.get("course_id", "") |  | ||||||
|             price_policy_id = request.data.get("price_policy_id", "") |  | ||||||
|             user_id = request.user.id |  | ||||||
|             # 2 验证数据的合法性 |  | ||||||
|             # 2.1 验证course_id是否合法 |  | ||||||
|             course_obj = Course.objects.get(pk=course_id) |  | ||||||
|             # 2.2 校验价格策略是否能合法 |  | ||||||
| 
 |  | ||||||
|             # 查找课程关联的价格策略 |  | ||||||
|             price_policy_list = course_obj.price_policy.all() |  | ||||||
|             price_policy_dict = {} |  | ||||||
|             for price_policy in price_policy_list: |  | ||||||
|                 price_policy_dict[price_policy.pk] = { |  | ||||||
|                     "prcie": price_policy.price, |  | ||||||
|                     "valid_period": price_policy.valid_period, |  | ||||||
|                     "valid_period_text": price_policy.get_valid_period_display(), |  | ||||||
|                     "default": price_policy.pk == price_policy_id |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|             if price_policy_id not in price_policy_dict: |  | ||||||
|                 raise CommonException(1001, "价格策略异常!") |  | ||||||
| 
 |  | ||||||
|                 # 3 构建我们想要的数据结构 |  | ||||||
|                 # 价格策略对象 |  | ||||||
|             pp = PricePolicy.objects.get(pk=price_policy_id) |  | ||||||
|             course_info = { |  | ||||||
|                 "id": course_id, |  | ||||||
|                 "name": course_obj.name, |  | ||||||
|                 "course_img": course_obj.course_img, |  | ||||||
|                 "relate_price_policy": price_policy_dict, |  | ||||||
|                 "default_price": pp.price, |  | ||||||
|                 "default_price_period": pp.valid_period, |  | ||||||
|                 "default_price_policy_id": pp.pk |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             # 4 写入redis |  | ||||||
|             # 4.1 先拼接购物车的key |  | ||||||
|             shopping_car_key = settings.SHOPPING_CAR_KEY % (user_id, course_id) |  | ||||||
|             # 4.2 写入redis |  | ||||||
|             print("------>") |  | ||||||
|             REDIS_CONN.set(shopping_car_key, json.dumps(course_info)) |  | ||||||
|             res.msg = "加入购物车成功!" |  | ||||||
| 
 |  | ||||||
|         except CommonException as e: |  | ||||||
|             res.code = e.code |  | ||||||
|             res.error = e.error |  | ||||||
|         except Exception as e: |  | ||||||
|             res.code = 1030 |  | ||||||
|             res.error = "加入购物车失败!" |  | ||||||
| 
 |  | ||||||
|         return Response(res.dict) |  | ||||||
| 
 |  | ||||||
|     def get(self, request): |  | ||||||
|         res = BaseResponse() |  | ||||||
|         try: |  | ||||||
|             # 1 取到user_id |  | ||||||
|             user_id = request.user.id |  | ||||||
|             # 2 拼接购物车的key |  | ||||||
|             shopping_car_key = settings.SHOPPING_CAR_KEY % (user_id, "*") |  | ||||||
|             print("shopping_car_key",shopping_car_key) |  | ||||||
|             # shopping_car_1_* |  | ||||||
|             # shopping_car_1_asdgnlaksdj |  | ||||||
|             # 3 去redis读取该用户的所有加入购物车的课程 |  | ||||||
|             # 3.1 先去模糊匹配出所有符合要求的key |  | ||||||
|             all_keys = REDIS_CONN.keys(shopping_car_key) |  | ||||||
|             print("all_keys",all_keys) |  | ||||||
|             # 3.2 循环所有的keys 得到每个可以 |  | ||||||
|             shopping_car_list = [] |  | ||||||
|             for key in all_keys: |  | ||||||
|                 print("key",key) |  | ||||||
|                 course_info = json.loads(REDIS_CONN.get(key)) |  | ||||||
|                 shopping_car_list.append(course_info) |  | ||||||
| 
 |  | ||||||
|             res.data = {"shopping_car_list": shopping_car_list, "total": len(shopping_car_list)} |  | ||||||
| 
 |  | ||||||
|         except Exception as e: |  | ||||||
|             res.code = 1033 |  | ||||||
|             res.error = "获取购物车失败" |  | ||||||
|         print("res.dict",res.dict) |  | ||||||
|         return Response(res.dict) |  | ||||||
| 
 |  | ||||||
|     def put(self, request): |  | ||||||
|         res = BaseResponse() |  | ||||||
|         try: |  | ||||||
|             # 1 获取前端传过来的course_id 以及price_policy_id |  | ||||||
|             course_id = request.data.get("course_id", "") |  | ||||||
|             price_policy_id = request.data.get("price_policy_id", "") |  | ||||||
|             user_id = request.user.id |  | ||||||
|             # 2 校验数据的合法性 |  | ||||||
|             # 2.1 校验course_id是否合法 |  | ||||||
|             shopping_car_key = settings.SHOPPING_CAR_KEY % (user_id, course_id) |  | ||||||
|             if not REDIS_CONN.exists(shopping_car_key): |  | ||||||
|                 res.code = 1035 |  | ||||||
|                 res.error = "课程不存在" |  | ||||||
|                 return Response(res.dict) |  | ||||||
|             # 2.2 判断价格策略是否合法 |  | ||||||
|             course_info = REDIS_CONN.hgetall(shopping_car_key) |  | ||||||
|             price_policy_dict = json.loads(course_info["price_policy_dict"]) |  | ||||||
|             if str(price_policy_id) not in price_policy_dict: |  | ||||||
|                 res.code = 1036 |  | ||||||
|                 res.error = "所选的价格策略不存在" |  | ||||||
|                 return Response(res.dict) |  | ||||||
|             # 3 修改redis中的default_policy_id |  | ||||||
|             course_info["default_policy_id"] = price_policy_id |  | ||||||
|             # 4 修改信息后写入redis |  | ||||||
|             REDIS_CONN.hmset(shopping_car_key, course_info) |  | ||||||
|             res.data = "更新成功" |  | ||||||
|         except Exception as e: |  | ||||||
|             res.code = 1034 |  | ||||||
|             res.error = "更新价格策略失败" |  | ||||||
|         return Response(res.dict) |  | ||||||
| 
 |  | ||||||
|     def delete(self, request): |  | ||||||
|         res = BaseResponse() |  | ||||||
|         try: |  | ||||||
|             # 获取前端传过来的course_id |  | ||||||
|             course_id = request.data.get("course_id", "") |  | ||||||
|             user_id = request.user.id |  | ||||||
|             # 判断课程id是否合法 |  | ||||||
|             shopping_car_key = settings.SHOPPING_CAR_KEY % (user_id, course_id) |  | ||||||
|             if not REDIS_CONN.exists(shopping_car_key): |  | ||||||
|                 res.code = 1039 |  | ||||||
|                 res.error = "删除的课程不存在" |  | ||||||
|                 return Response(res.dict) |  | ||||||
|             # 删除redis中的数据 |  | ||||||
|             REDIS_CONN.delete(shopping_car_key) |  | ||||||
|             res.data = "删除成功" |  | ||||||
|         except Exception as e: |  | ||||||
|             res.code = 1037 |  | ||||||
|             res.error = "删除失败" |  | ||||||
|         return Response(res.dict) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ''' |  | ||||||
| 1 post接口构建数据结构: |  | ||||||
|     { |  | ||||||
|         "id": 2, |  | ||||||
|         "default_price_period": 14, |  | ||||||
|         "relate_price_policy": { |  | ||||||
|             "1": { |  | ||||||
|                 "valid_period": 7, |  | ||||||
|                 "valid_period_text": "1周", |  | ||||||
|                 "default": false, |  | ||||||
|                 "prcie": 100.0 |  | ||||||
|             }, |  | ||||||
|             "2": { |  | ||||||
|                 "valid_period": 14, |  | ||||||
|                 "valid_period_text": "2周", |  | ||||||
|                 "default": true, |  | ||||||
|                 "prcie": 200.0 |  | ||||||
|             }, |  | ||||||
|             "3": { |  | ||||||
|                 "valid_period": 30, |  | ||||||
|                 "valid_period_text": "1个月", |  | ||||||
|                 "default": false, |  | ||||||
|                 "prcie": 300.0 |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         "name": "Django框架学习", |  | ||||||
|         "course_img": "https://luffycity.com/static/frontend/course/3/Django框架学习_1509095212.759272.png", |  | ||||||
|         "default_price": 200.0 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 2 get接口查询数据结构: |  | ||||||
| 
 |  | ||||||
| { |  | ||||||
|     "data": { |  | ||||||
|         "total": 2, |  | ||||||
|         "shopping_car_list": [ |  | ||||||
|             { |  | ||||||
|                 "id": 2, |  | ||||||
|                 "default_price_period": 14, |  | ||||||
|                 "relate_price_policy": { |  | ||||||
|                     "1": { |  | ||||||
|                         "valid_period": 7, |  | ||||||
|                         "valid_period_text": "1周", |  | ||||||
|                         "default": false, |  | ||||||
|                         "prcie": 100 |  | ||||||
|                     }, |  | ||||||
|                     "2": { |  | ||||||
|                         "valid_period": 14, |  | ||||||
|                         "valid_period_text": "2周", |  | ||||||
|                         "default": true, |  | ||||||
|                         "prcie": 200 |  | ||||||
|                     }, |  | ||||||
|                     "3": { |  | ||||||
|                         "valid_period": 30, |  | ||||||
|                         "valid_period_text": "1个月", |  | ||||||
|                         "default": false, |  | ||||||
|                         "prcie": 300 |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 "name": "Django框架学习", |  | ||||||
|                 "course_img": "https://luffycity.com/static/frontend/course/3/Django框架学习_1509095212.759272.png", |  | ||||||
|                 "default_price": 200 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "id": 4, |  | ||||||
|                 "default_price_period": 30, |  | ||||||
|                 "relate_price_policy": { |  | ||||||
|                     "4": { |  | ||||||
|                         "valid_period": 30, |  | ||||||
|                         "valid_period_text": "1个月", |  | ||||||
|                         "default": true, |  | ||||||
|                         "prcie": 1000 |  | ||||||
|                     }, |  | ||||||
|                     "5": { |  | ||||||
|                         "valid_period": 60, |  | ||||||
|                         "valid_period_text": "2个月", |  | ||||||
|                         "default": false, |  | ||||||
|                         "prcie": 1500 |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 "name": "Linux系统基础5周入门精讲", |  | ||||||
|                 "course_img": "https://luffycity.com/static/frontend/course/12/Linux5周入门_1509589530.6144893.png", |  | ||||||
|                 "default_price": 1000 |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     "code": 1000, |  | ||||||
|     "msg": "" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ''' |  | ||||||
| @ -1,114 +0,0 @@ | |||||||
| 
 |  | ||||||
| from rest_framework.views import APIView |  | ||||||
| from api.utils.ali.api import ali_api |  | ||||||
| from api.utils.ali.tools import verify_signature |  | ||||||
| from rest_framework.response import Response |  | ||||||
| from api.models import Order |  | ||||||
| import datetime,time |  | ||||||
| from django.shortcuts import HttpResponse,redirect |  | ||||||
| from api.utils.response import BaseResponse |  | ||||||
| 
 |  | ||||||
| class AlipayTradeView(APIView): |  | ||||||
| 
 |  | ||||||
|     def get(self,request,*args,**kwargs): |  | ||||||
| 
 |  | ||||||
|        try: |  | ||||||
|             processed_dict = {} |  | ||||||
|             for key, value in self.request.query_params.items(): |  | ||||||
|                 processed_dict[key] = value |  | ||||||
|             # 校验签名 |  | ||||||
|             verify_result = verify_signature(processed_dict, ali_api.pay.ali_public_key) |  | ||||||
| 
 |  | ||||||
|             if not verify_result: |  | ||||||
|                 return HttpResponse("sign is invalid") |  | ||||||
| 
 |  | ||||||
|             out_trade_no = processed_dict.get("out_trade_no")  # 商户订单号 |  | ||||||
|             redirect_to = "{0}?order_num={1}".format("http://47.94.172.250:8804/order/pay_success",out_trade_no) |  | ||||||
|             return redirect(redirect_to) |  | ||||||
| 
 |  | ||||||
|        except Exception as e: |  | ||||||
|           return HttpResponse("fail!") |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     def post(self, request): |  | ||||||
|         """ |  | ||||||
|         处理支付宝的notify_url |  | ||||||
| 
 |  | ||||||
|         支付宝对应交易的四种状态: |  | ||||||
|             1,WAIT_BUYER_PAY    交易创建,等待买家付款 |  | ||||||
|             2,TRADE_CLOSED      未付款交易超时关闭,或支付完成后全额退款 |  | ||||||
|             3,TRADE_SUCCESS     交易支付成功 |  | ||||||
|             4,TRADE_FINISHED    交易结束,不可退款 |  | ||||||
| 
 |  | ||||||
|         如果支付成功, 将要处理的事件: |  | ||||||
|             1, 订单状态更改为交易完成(变更状态后, 通过信号进行如下操作) |  | ||||||
|             2, 如果使用优惠券, 支付成功将要把优惠券的状态更改为已使用 |  | ||||||
|             3, 如果使用余额, 将要进行扣除 |  | ||||||
|         """ |  | ||||||
|         processed_dict = {} |  | ||||||
|         for key, value in self.request.data.items(): |  | ||||||
|             processed_dict[key] = value |  | ||||||
|         # 校验签名 |  | ||||||
|         verify_result = verify_signature(processed_dict, ali_api.pay.ali_public_key) |  | ||||||
| 
 |  | ||||||
|         if not verify_result: |  | ||||||
|             return Response("fail") |  | ||||||
| 
 |  | ||||||
|         order_sn = processed_dict.get("out_trade_no", "")  # 商户网站唯一订单号 |  | ||||||
|         trade_no = processed_dict.get("trade_no", "")  # 该交易在支付宝系统中的交易流水号。最长64位 |  | ||||||
|         trade_status = processed_dict.get("trade_status", "")  # 支付宝系统的交易状态 |  | ||||||
|         # 支付成功 |  | ||||||
|         #   为放止支付宝重复请求, 获取到订单号并查询该订单的状态是否为交易完成, 如果交易完成, 即直接返回成功信号 |  | ||||||
|         #   为该用户创建报名课程, 创建报名时间及结束时间 |  | ||||||
|         #   区分LuffyX课程 和 付费课程 |  | ||||||
|         #   当前策略LuffyX课程是不能加入购物车的 |  | ||||||
|         if trade_status == "TRADE_SUCCESS": |  | ||||||
|             gmt_payment = processed_dict.get("gmt_payment")  # 买家付款时间 格式 yyyy-MM-dd HH:mm:ss |  | ||||||
|             passback_params = processed_dict.get("passback_params", "{}")  # 公共回传参数 |  | ||||||
| 
 |  | ||||||
|             # 修改订单状态 |  | ||||||
|             save_status = self.change_order_status(order_sn, trade_no, gmt_payment, "alipay",passback_params) |  | ||||||
|             if save_status is True: |  | ||||||
|                 return Response("success") |  | ||||||
|         return Response("fail") |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     def change_order_status(order_num, payment_number, gmt_payment, trade_type, extra_params): |  | ||||||
|         """交易成功修改订单相关的状态 |  | ||||||
| 
 |  | ||||||
|         Parameters |  | ||||||
|         ---------- |  | ||||||
|         order_num : string |  | ||||||
|             订单号 |  | ||||||
| 
 |  | ||||||
|         payment_number : string or None |  | ||||||
|             第三方订单号 |  | ||||||
| 
 |  | ||||||
|         gmt_payment : string |  | ||||||
|             交易时间(要根据不同的交易方式格式化交易时间) |  | ||||||
| 
 |  | ||||||
|         trade_type: string |  | ||||||
|             交易方式 |  | ||||||
| 
 |  | ||||||
|         extra_params: string json |  | ||||||
|             交易回传参数 |  | ||||||
| 
 |  | ||||||
|         Returns |  | ||||||
|         ------- |  | ||||||
|         bool |  | ||||||
|         """ |  | ||||||
|         try: |  | ||||||
|             exist_order = Order.objects.get(order_number=order_num) |  | ||||||
|             pay_time = datetime.datetime.strptime(gmt_payment, "%Y-%m-%d %H:%M:%S") |  | ||||||
| 
 |  | ||||||
|             if exist_order.status == 0: |  | ||||||
|                 return True |  | ||||||
|             # 变更订单状态 |  | ||||||
|             exist_order.payment_number = payment_number |  | ||||||
|             exist_order.status = 0 |  | ||||||
|             exist_order.pay_time = pay_time |  | ||||||
|             exist_order.save(update_fields=("payment_number", "status", "pay_time", ),) |  | ||||||
|         except Exception as e: |  | ||||||
|             pass |  | ||||||
| 
 |  | ||||||
|         return True |  | ||||||
| @ -1,45 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> |  | ||||||
| <plist version="1.0"><dict> |  | ||||||
|   <key>items</key> |  | ||||||
|   <array> |  | ||||||
|     <dict> |  | ||||||
|       <key>assets</key> |  | ||||||
|       <array> |  | ||||||
|         <dict> |  | ||||||
|           <key>kind</key> |  | ||||||
|           <string>software-package</string> |  | ||||||
|           <key>url</key> |  | ||||||
|           <string><![CDATA[https://ali-fir-pro-binary.jappstore.com/52e972d2749a404028e2ab7edb04d7a7f365184d?auth_key=1583720494-0-0-3dc9d1bf4f093c53f1193614ee5f6d3e]]></string> |  | ||||||
|         </dict> |  | ||||||
|         <dict> |  | ||||||
|           <key>kind</key> |  | ||||||
|           <string>display-image</string> |  | ||||||
|           <key>needs-shine</key> |  | ||||||
|           <integer>0</integer> |  | ||||||
|           <key>url</key> |  | ||||||
|           <string><![CDATA[https://ali-fir-pro-icon.fir.im/6be40db9e2bb729db1ce0db93b8ebdba50ea8b4d?auth_key=1583720494-0-0-0212635dc7c21a3c02cd2422d0213e99]]></string> |  | ||||||
|         </dict> |  | ||||||
|         <dict> |  | ||||||
|           <key>kind</key> |  | ||||||
|           <string>full-size-image</string> |  | ||||||
|           <key>needs-shine</key> |  | ||||||
|           <true/> |  | ||||||
|           <key>url</key> |  | ||||||
|           <string><![CDATA[https://ali-fir-pro-icon.fir.im/6be40db9e2bb729db1ce0db93b8ebdba50ea8b4d?auth_key=1583720494-0-0-0212635dc7c21a3c02cd2422d0213e99]]></string> |  | ||||||
|         </dict> |  | ||||||
|       </array> |  | ||||||
|       <key>metadata</key> |  | ||||||
|       <dict> |  | ||||||
|         <key>bundle-identifier</key> |  | ||||||
|         <string>com.fengxin.im.test</string> |  | ||||||
|         <key>bundle-version</key> |  | ||||||
|         <string><![CDATA[1.1.7]]></string> |  | ||||||
|         <key>kind</key> |  | ||||||
|         <string>software</string> |  | ||||||
|         <key>title</key> |  | ||||||
|         <string><![CDATA[亲友圈发发]]></string> |  | ||||||
|       </dict> |  | ||||||
|     </dict> |  | ||||||
|   </array> |  | ||||||
| </dict> |  | ||||||
| </plist> |  | ||||||
					Loading…
					
					
				
		Reference in new issue