add aliyun and qiniuyun oss

storage_cloud
nineven 5 years ago
parent a20d3c7dff
commit f9de02f139
  1. 2
      fir_client/package-lock.json
  2. 1
      fir_client/package.json
  3. 1
      fir_client/src/components/FirAppInfosBase.vue
  4. 12
      fir_client/src/components/FirAppInfosbaseinfo.vue
  5. 8
      fir_client/src/components/FirAppInfostimeline.vue
  6. 108
      fir_client/src/components/FirApps.vue
  7. 31
      fir_client/src/restful/index.js
  8. 54
      fir_client/src/utils/index.js
  9. 570
      fir_client/yarn.lock
  10. 1
      fir_ser/api/admin.py
  11. 44
      fir_ser/api/migrations/0013_auto_20200323_2020.py
  12. 19
      fir_ser/api/migrations/0014_userinfo_storage.py
  13. 28
      fir_ser/api/models.py
  14. 192
      fir_ser/api/tests.py
  15. 8
      fir_ser/api/urls.py
  16. 3
      fir_ser/api/utils/TokenManager.py
  17. 0
      fir_ser/api/utils/app/__init__.py
  18. 84
      fir_ser/api/utils/app/apputils.py
  19. 0
      fir_ser/api/utils/app/randomstrings.py
  20. 10
      fir_ser/api/utils/exceptions.py
  21. 23
      fir_ser/api/utils/filter.py
  22. 219
      fir_ser/api/utils/serializer.py
  23. 113
      fir_ser/api/utils/storage/aliyunApi.py
  24. 34
      fir_ser/api/utils/storage/localApi.py
  25. 29
      fir_ser/api/utils/storage/qiniuApi.py
  26. 60
      fir_ser/api/utils/storage/storage.py
  27. 465
      fir_ser/api/views/account.py
  28. 196
      fir_ser/api/views/apps.py
  29. 41
      fir_ser/api/views/course.py
  30. 116
      fir_ser/api/views/download.py
  31. 89
      fir_ser/api/views/localuploads.py
  32. 9
      fir_ser/api/views/login.py
  33. 42
      fir_ser/api/views/order.py
  34. 263
      fir_ser/api/views/payment.py
  35. 258
      fir_ser/api/views/shoppingcart.py
  36. 114
      fir_ser/api/views/trade.py
  37. 60
      fir_ser/api/views/uploads.py
  38. 45
      fir_ser/api/xmlfir.xml
  39. 2
      fir_ser/fir_ser/urls.py

@ -1,5 +1,5 @@
{ {
"name": "fir_test", "name": "fir_client",
"version": "0.1.0", "version": "0.1.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,

@ -8,6 +8,7 @@
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"ali-oss": "^6.5.1",
"app-info-parser": "^0.3.9", "app-info-parser": "^0.3.9",
"axios": "^0.19.0", "axios": "^0.19.0",
"core-js": "^3.4.4", "core-js": "^3.4.4",

@ -161,6 +161,7 @@
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log("失败了"); console.log("失败了");
} }
}, { }, {
"app_id": this.$route.params.id "app_id": this.$route.params.id
}); });

@ -60,7 +60,7 @@
</template> </template>
<script> <script>
import {deleteapp, getappinfos, getapppicurl, updateapp} from "../restful" import {deleteapp, getappinfos, getapppicurl, getuploadToken, updateapp} from "../restful"
export default { export default {
name: "FirAppInfosbaseinfo", name: "FirAppInfosbaseinfo",
@ -150,7 +150,15 @@
const isLt2M = file.size / 1024 / 1024 < 2; const isLt2M = file.size / 1024 / 1024 < 2;
if(file.type === 'image/jpeg' || file.type === 'image/png'|| file.type === 'image/jpg'){ if(file.type === 'image/jpeg' || file.type === 'image/png'|| file.type === 'image/jpg'){
if (isLt2M) { if (isLt2M) {
return true; // return true;
getuploadToken(data => {
if (data.code === 1000) {
// eslint-disable-next-line no-console
console.log(data.data)
}
},{'methods':false,'data':{'app_id':this.currentapp.app_id,'upload_key':file.name}});
} }
else{ else{
this.$message.error('上传头像图片大小不能超过 2MB!'); this.$message.error('上传头像图片大小不能超过 2MB!');

@ -145,7 +145,12 @@
}, },
getapptimelineFun() { getapptimelineFun() {
const loading = this.$loading({
lock: true,
text: '加载中',
spinner: 'el-icon-loading',
// background: 'rgba(0, 0, 0, 0.7)'
});
getapptimeline(data => { getapptimeline(data => {
if (data.code === 1000) { if (data.code === 1000) {
this.release_apps = data.data.release_apps; this.release_apps = data.data.release_apps;
@ -153,6 +158,7 @@
} else if (data.code === 1003) { } else if (data.code === 1003) {
this.$router.push({name: 'FirApps'}); this.$router.push({name: 'FirApps'});
} }
loading.close();
}, { }, {
"app_id": this.$route.params.id, "app_id": this.$route.params.id,
"action": "timeline", "action": "timeline",

@ -376,7 +376,7 @@
<script> <script>
import {getapps, deleteapp,getuploadToken} from "../restful"; import {getapps, deleteapp,getuploadToken} from "../restful";
import {getScrollHeight,getScrollTop,getWindowHeight,getappinfo,uploadqiniuoss,dataURLtoFile} from "../utils"; import {getScrollHeight,getScrollTop,getWindowHeight,getappinfo,uploadqiniuoss,dataURLtoFile,uploadaliyunoss,uploadlocalstorage} from "../utils";
export default { export default {
name: "FirApps", name: "FirApps",
@ -404,28 +404,72 @@
uploadsuccess:0 uploadsuccess:0
} }
}, methods: { }, methods: {
uploadqiniu(file,certinfo){ uploadtostorage(file,certinfo){
// eslint-disable-next-line no-unused-vars
uploadqiniuoss(file,certinfo,this,res=>{ if(this.analyseappinfo.storage === 1){
this.uploadsuccess +=1; // eslint-disable-next-line no-unused-vars,no-unreachable
if(this.uploadsuccess === 2){ uploadqiniuoss(file,certinfo,this,res=>{
this.$message.success(file.name + '上传成功'); this.uploadsuccess +=1;
getuploadToken(data => { if(this.uploadsuccess === 2){
if (data.code === 1000) { this.$message.success(file.name + '上传成功');
let app_uuid = this.analyseappinfo.app_uuid; getuploadToken(data => {
this.closeUpload(); if (data.code === 1000) {
// app.getappsFun({}); let app_uuid = this.analyseappinfo.app_uuid;
this.$router.push({name: 'FirAppInfostimeline', params: {id: app_uuid}}); this.closeUpload();
} this.$router.push({name: 'FirAppInfostimeline', params: {id: app_uuid}});
},{'methods':'PUT','data':this.analyseappinfo}); }
} },{'methods':'PUT','data':this.analyseappinfo});
},process=>{ }
if(this.uploadsuccess === 1) { },process=>{
this.uploadprocess = process; if(this.uploadsuccess === 1) {
} this.uploadprocess = process;
}) }
})
}else if(this.analyseappinfo.storage === 2){
// eslint-disable-next-line no-unused-vars
uploadaliyunoss(file,certinfo,this,res=>{
this.uploadsuccess +=1;
if(this.uploadsuccess === 2){
this.$message.success(file.name + '上传成功');
getuploadToken(data => {
if (data.code === 1000) {
let app_uuid = this.analyseappinfo.app_uuid;
this.closeUpload();
this.$router.push({name: 'FirAppInfostimeline', params: {id: app_uuid}});
}
},{'methods':'PUT','data':this.analyseappinfo});
}
},process=>{
if(this.uploadsuccess === 1) {
this.uploadprocess = process;
}
});
}else {
//
certinfo.upload_url = this.uploadconf.UploadUrl;
// eslint-disable-next-line no-unused-vars,no-unreachable
uploadlocalstorage(file,certinfo,this,res=>{
this.uploadsuccess +=1;
if(this.uploadsuccess === 2){
this.$message.success(file.name + '上传成功');
getuploadToken(data => {
if (data.code === 1000) {
let app_uuid = this.analyseappinfo.app_uuid;
this.closeUpload();
this.$router.push({name: 'FirAppInfostimeline', params: {id: app_uuid}});
}
},{'methods':'PUT','data':this.analyseappinfo});
}
},process=>{
if(this.uploadsuccess === 1) {
this.uploadprocess = process;
}
})
}
}, },
getuploadtoken(){ getuploadtoken(loading){
getuploadToken(data =>{ getuploadToken(data =>{
if(data.code === 1000){ if(data.code === 1000){
this.analyseappinfo.short = data.data.short; this.analyseappinfo.short = data.data.short;
@ -435,11 +479,13 @@
this.analyseappinfo.upload_key= data.data.upload_key; this.analyseappinfo.upload_key= data.data.upload_key;
this.analyseappinfo.png_key= data.data.png_key; this.analyseappinfo.png_key= data.data.png_key;
this.analyseappinfo.png_token= data.data.png_token; this.analyseappinfo.png_token= data.data.png_token;
this.analyseappinfo.storage= data.data.storage;
this.willuploadApp=true; this.willuploadApp=true;
}else { }else {
this.$message.error("上传token获取失败,请刷新重试") this.$message.error("上传token获取失败,请刷新重试")
} }
loading.close();
},{'methos':true,'data':{"bundleid":this.analyseappinfo.bundleid,"type":this.analyseappinfo.type}}) },{'methos':true,'data':{"bundleid":this.analyseappinfo.bundleid,"type":this.analyseappinfo.type}})
}, },
uploadcloud(){ uploadcloud(){
@ -447,10 +493,11 @@
this.uploading = true; this.uploading = true;
let file=dataURLtoFile(this.analyseappinfo.icon,this.analyseappinfo.png_key); let file=dataURLtoFile(this.analyseappinfo.icon,this.analyseappinfo.png_key);
this.uploadqiniu(file,{'upload_key':this.analyseappinfo.png_key,'upload_token':this.analyseappinfo.png_token});
this.uploadtostorage(file,{'upload_key':this.analyseappinfo.png_key,'upload_token':this.analyseappinfo.png_token});
file = this.currentfile; file = this.currentfile;
this.uploadqiniu(file,{'upload_key':this.analyseappinfo.upload_key,'upload_token':this.analyseappinfo.upload_token}); this.uploadtostorage(file,{'upload_key':this.analyseappinfo.upload_key,'upload_token':this.analyseappinfo.upload_token});
}, },
closeUpload(){ closeUpload(){
this.uploadsuccess = 0; this.uploadsuccess = 0;
@ -530,9 +577,15 @@
}, },
getappsFun(parms) { getappsFun(parms) {
const loading = this.$loading({
lock: true,
text: '加载中',
spinner: 'el-icon-loading',
// background: 'rgba(0, 0, 0, 0.7)'
});
getapps(data => { getapps(data => {
if (data.code === 1000) { if (data.code === 1000) {
loading.close();
if(this.firstloadflag){ if(this.firstloadflag){
window.addEventListener('scroll',this.auto_load); window.addEventListener('scroll',this.auto_load);
this.firstloadflag = false this.firstloadflag = false
@ -587,19 +640,18 @@
},beforeAvatarUpload(file){ },beforeAvatarUpload(file){
const loading = this.$loading({ const loading = this.$loading({
lock: true, lock: true,
text: 'Loading', text: '应用解析中',
spinner: 'el-icon-loading', spinner: 'el-icon-loading',
// background: 'rgba(0, 0, 0, 0.7)' // background: 'rgba(0, 0, 0, 0.7)'
}); });
getappinfo(file,appinfo =>{ getappinfo(file,appinfo =>{
if(appinfo.bundleid){ if(appinfo.bundleid){
this.analyseappinfo = appinfo; this.analyseappinfo = appinfo;
this.getuploadtoken(); this.getuploadtoken(loading);
this.currentfile = file; this.currentfile = file;
}else { }else {
this.$message.error("应用解析失败,请检查是否为APP应用") this.$message.error("应用解析失败,请检查是否为APP应用")
} }
loading.close();
},err=>{ },err=>{
loading.close(); loading.close();
this.$message.error("应用解析失败,请检查是否为APP应用"); this.$message.error("应用解析失败,请检查是否为APP应用");

@ -12,9 +12,9 @@ Axios.defaults.httpsAgent = new https.Agent({
}); });
// Axios.defaults.baseURL=''; // Axios.defaults.baseURL='';
const USERSEVER = 'https://fir.dvcloud.xin/api/v1/fir/server'; // const USERSEVER = 'https://fir.dvcloud.xin/api/v1/fir/server';
// const USERSEVER = 'http://192.168.1.112:8000/api/v1/fir/server'; const USERSEVER = 'http://192.168.1.112:8000/api/v1/fir/server';
export function set_auth_token() { export function set_auth_token() {
@ -596,3 +596,30 @@ export function getuploadToken(callBack, params, load = true) {
true true
); );
} }
export function uploadstorage(certinfo,file,successCallback,processCallback) {
let config = {
onUploadProgress: function (progressEvent) {
let total=progressEvent.total;
let loaded = progressEvent.loaded;
processCallback(Math.round(loaded*100/total))
},
headers: {
'Content-Type': 'multipart/form-data',
'Authorization':VueCookies.get('auth_token'),
},
};
const data = new FormData();
data.append('file', file);
data.append('certinfo',JSON.stringify(certinfo));
Axios.post(certinfo.upload_url, data, config).then(res => {
// eslint-disable-next-line no-console
console.log(res);
successCallback(res)
}).catch(err => {
// eslint-disable-next-line no-console
console.log(err);
});
}

@ -29,7 +29,6 @@ export function getScrollHeight(){
} }
//浏览器视口的高度 //浏览器视口的高度
export function getWindowHeight(){ export function getWindowHeight(){
let windowHeight = 0; let windowHeight = 0;
@ -125,3 +124,56 @@ export function dataURLtoFile(dataurl, filename) {//将base64转换为文件
} }
return new File([u8arr], filename, {type:mime}); return new File([u8arr], filename, {type:mime});
} }
export function uploadaliyunoss(file,certinfo,app,successcallback,processcallback) {
const OSS = require('ali-oss');
let token = certinfo.upload_token;
let client = new OSS({
endpoint: token.endpoint,
accessKeyId: token.access_key_id,
accessKeySecret: token.access_key_secret,
stsToken: token.security_token,
bucket: token.bucket
});
// eslint-disable-next-line no-unused-vars
let currentCheckpoint;
const progress = async function progress(p, checkpoint) {
currentCheckpoint = checkpoint;
// eslint-disable-next-line no-console
console.log(Math.floor(p * 100));
// eslint-disable-next-line no-console
console.log(checkpoint);
processcallback(Math.floor(p * 100));
};
const options = {
progress,
partSize: 1000 * 1024,
// meta: {
// year: 2017,
// people: 'test',
// },
};
client.multipartUpload(certinfo.upload_key, file, options).then( (res) => {
// eslint-disable-next-line no-console
console.log('upload success: %j', res);
successcallback(res);
currentCheckpoint = null;
}).catch((err) => {
// eslint-disable-next-line no-console
console.error(err);
app.$message({
message:file.name + '上传失败,请刷新页面重试',
type: 'error',
duration:0
});
});
}
import {uploadstorage} from '../restful'
export function uploadlocalstorage(file,certinfo,app,successcallback,processcallback) {
uploadstorage(certinfo,file,successcallback,processcallback)
}

File diff suppressed because it is too large Load Diff

@ -9,4 +9,5 @@ admin.site.register(Apps)
admin.site.register(AppReleaseInfo) admin.site.register(AppReleaseInfo)
admin.site.register(Token) admin.site.register(Token)
admin.site.register(VerifyName) admin.site.register(VerifyName)
admin.site.register(AppStorage)

@ -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='存储'),
),
]

@ -3,9 +3,7 @@ from django.db import models
# Create your models here. # Create your models here.
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from api.utils.app.randomstrings import make_random_uuid
from django.utils.safestring import mark_safe
from api.utils.randomstrings import make_random_uuid
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
######################################## 用户表 ######################################## ######################################## 用户表 ########################################
@ -43,6 +41,8 @@ class UserInfo(AbstractUser):
download_times = models.IntegerField(default=100, verbose_name="下载次数") download_times = models.IntegerField(default=100, verbose_name="下载次数")
all_download_times = models.IntegerField(default=0, verbose_name="总共下载次数") all_download_times = models.IntegerField(default=0, verbose_name="总共下载次数")
domain_name = models.CharField(verbose_name="域名",blank=True,null=True,max_length=64) domain_name = models.CharField(verbose_name="域名",blank=True,null=True,max_length=64)
storage = models.OneToOneField(to='AppStorage',related_name='app_storage',
on_delete=models.SET_NULL, verbose_name="存储",null=True)
# is_staff = models.BooleanField(verbose_name='staff status', default=False, help_text='决定着用户是否可登录管理后台') # is_staff = models.BooleanField(verbose_name='staff status', default=False, help_text='决定着用户是否可登录管理后台')
# weixin = models.CharField(max_length=128, blank=True, null=True, db_index=True, verbose_name="微信") # weixin = models.CharField(max_length=128, blank=True, null=True, db_index=True, verbose_name="微信")
@ -122,7 +122,7 @@ class Apps(models.Model):
class AppReleaseInfo(models.Model): class AppReleaseInfo(models.Model):
is_master = models.BooleanField(verbose_name="是否master版本",default=True) is_master = models.BooleanField(verbose_name="是否master版本",default=True)
release_id = models.CharField(max_length=64, unique=True,verbose_name="release 版本id") release_id = models.CharField(max_length=64, unique=True,verbose_name="release 版本id")
app_id = models.ForeignKey(to="Apps",on_delete=models.CASCADE,verbose_name="属于哪个APP") app_id = models.ForeignKey(to="Apps",on_delete=models.CASCADE,verbose_name="属于哪个APP")
build_version = models.CharField(max_length=16,verbose_name="build版本",blank=True) build_version = models.CharField(max_length=16,verbose_name="build版本",blank=True)
@ -145,3 +145,23 @@ class AppReleaseInfo(models.Model):
return "%s" % (self.release_id) return "%s" % (self.release_id)
class AppStorage(models.Model):
user_id = models.ForeignKey(to="UserInfo",verbose_name="用户ID",on_delete=models.CASCADE)
name = models.CharField(max_length=64,blank=True, null=True, verbose_name="存储名字")
# is_used = models.BooleanField(verbose_name="是否使用该存储",default=True)
storage_choices=((0,'本地存储'),(1,'七牛云存储'),(2,'阿里云存储'),(3,'默认存储'))
storage_type = models.SmallIntegerField(choices=storage_choices, default=3, verbose_name="存储类型")
access_key =models.CharField(max_length=128,blank=True, null=True, verbose_name="存储访问key")
secret_key =models.CharField(max_length=128,blank=True, null=True, verbose_name="存储访问secret")
bucket_name=models.CharField(max_length=128,blank=True, null=True, verbose_name="存储空间bucket_name")
additionalparameters = models.TextField(max_length=256,blank=True, null=True,verbose_name="额外参数",
help_text='阿里云:{"sts_role_arn":"arn信息","endpoint":""} 七牛云:{"domain_name":""}')
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
description = models.TextField('备注', blank=True, null=True, default=None, )
class Meta:
verbose_name = '存储配置'
verbose_name_plural = "存储配置"
def __str__(self):
return "%s %s" % (self.name,self.user_id.get_username())

@ -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)

@ -19,10 +19,10 @@ from django.urls import path,re_path,include
from api.views.login import LoginView,UserInfoView from api.views.login import LoginView,UserInfoView
from api.views.logout import LogoutView from api.views.logout import LogoutView
from api.views.captcha import CaptchaView from api.views.captcha import CaptchaView
from api.views.apps import AppsView,AppinfoView,AppReleaseinfoView from api.views.apps import AppsView,AppInfoView,AppReleaseinfoView
from api.views.localuploads import UploadView,UploadImgView from api.views.localuploads import UploadView,UploadImgView
from api.views.download import DownloadTokenView from api.views.download import DownloadTokenView
from api.views.qiniuuploads import QiNiuUploadView from api.views.uploads import OssUploadView
# router=DefaultRouter() # router=DefaultRouter()
@ -34,11 +34,11 @@ urlpatterns = [
re_path("^logout",LogoutView.as_view()), re_path("^logout",LogoutView.as_view()),
re_path("^captcha_check/",CaptchaView.as_view()), re_path("^captcha_check/",CaptchaView.as_view()),
re_path("^apps$", AppsView.as_view()), re_path("^apps$", AppsView.as_view()),
re_path("^apps/(?P<app_id>\w+)", AppinfoView.as_view()), re_path("^apps/(?P<app_id>\w+)", AppInfoView.as_view()),
re_path("^appinfos/(?P<app_id>\w+)/(?P<act>\w+)", AppReleaseinfoView.as_view()), re_path("^appinfos/(?P<app_id>\w+)/(?P<act>\w+)", AppReleaseinfoView.as_view()),
re_path("^upload$",UploadView.as_view()), re_path("^upload$",UploadView.as_view()),
re_path("^img/upload$",UploadImgView.as_view()), re_path("^img/upload$",UploadImgView.as_view()),
re_path("^userinfo",UserInfoView.as_view()), re_path("^userinfo",UserInfoView.as_view()),
re_path("^download_token/(?P<short>\w+)$", DownloadTokenView.as_view()), re_path("^download_token/(?P<short>\w+)$", DownloadTokenView.as_view()),
re_path("^qiniuupload$", QiNiuUploadView.as_view()), re_path("^qiniuupload$", OssUploadView.as_view()),
] ]

@ -21,7 +21,7 @@ class DownloadToken(object):
def make_token(self,release_id,time_limit=60): def make_token(self,release_id,time_limit=60):
random_str = uuid.uuid1().__str__().split("-")[0:-1] random_str = uuid.uuid1().__str__().split("-")[0:-1]
user_ran_str = uuid.uuid5(uuid.NAMESPACE_DNS, release_id[0]).__str__().split("-") user_ran_str = uuid.uuid5(uuid.NAMESPACE_DNS, release_id).__str__().split("-")
user_ran_str.extend(random_str) user_ran_str.extend(random_str)
new_str = "".join(user_ran_str) new_str = "".join(user_ran_str)
self.TokenLists.append({new_str:{ self.TokenLists.append({new_str:{
@ -56,7 +56,6 @@ class DownloadToken(object):
for token, data in itokendic.items(): for token, data in itokendic.items():
if int(ntime) - int(data["atime"]) > 0: if int(ntime) - int(data["atime"]) > 0:
self.TokenLists.remove(itokendic) self.TokenLists.remove(itokendic)
print("开始清理token,现在token:%s 清理token:%s 剩余token:%s"%(ncount,ncount-len(self.TokenLists),len(self.TokenLists))) print("开始清理token,现在token:%s 清理token:%s 剩余token:%s"%(ncount,ncount-len(self.TokenLists),len(self.TokenLists)))
time.sleep(30) time.sleep(30)

@ -3,24 +3,10 @@
# project: 3月 # project: 3月
# author: liuyu # author: liuyu
# date: 2020/3/6 # date: 2020/3/6
from fir_ser.settings import MEDIA_ROOT,MEDIA_URL from api.utils.app.randomstrings import make_app_uuid
import os
from .firApi import AppInfo
from api.utils.randomstrings import make_app_uuid
from api.models import AppReleaseInfo,Apps from api.models import AppReleaseInfo,Apps
import random,xmltodict,json import random,xmltodict,json
from .TokenManager import DownloadToken from api.utils.storage.storage import Storage
from api.utils.qiniu.tools import QiNiu
def make_download_token(release_id,time_limit):
release_ids = []
if release_id:
release_ids = [release_id]
dtoken = DownloadToken()
download_token = dtoken.make_token(release_ids,time_limit)
return download_token
def make_resigned(bin_url,img_url,bundle_id,bundle_version,name): def make_resigned(bin_url,img_url,bundle_id,bundle_version,name):
@ -109,42 +95,6 @@ def bytes2human(n):
return '%sB' % n return '%sB' % n
def delete_apps_storage(app_file_name,release_type):
app_path = os.path.join(MEDIA_ROOT,"apps")
icon_path = os.path.join(MEDIA_ROOT,"icons")
try:
if release_type == 0:
app_file_full_name="%s.apk" %app_file_name
else:
app_file_full_name="%s.ipa" %app_file_name
qiniu = QiNiu()
qiniu.del_qiniu_file(app_file_full_name)
icon_url = AppReleaseInfo.objects.filter(release_id=app_file_name).first().icon_url
qiniu.del_qiniu_file(os.path.basename(icon_url))
remove_lists=[
os.path.join(app_path, app_file_full_name),
os.path.join(icon_path, "%s.png" % (app_file_name))
]
for delfiles in remove_lists:
os.remove(delfiles)
except Exception as e:
print(e)
def delete_apps_icon_storage(app_file_name,type='icons'):
icon_path = os.path.join(MEDIA_ROOT,type)
qiniu = QiNiu()
qiniu.del_qiniu_file(app_file_name)
try:
os.remove(os.path.join(icon_path, app_file_name))
except Exception as e:
print(e)
def get_release_type(app_file_name,appinfo): def get_release_type(app_file_name,appinfo):
extension = app_file_name.split(".")[1] extension = app_file_name.split(".")[1]
if extension == "ipa": if extension == "ipa":
@ -180,29 +130,6 @@ def get_random_short():
def AnalyzeUtil(app_file_name,request):
user_obj = request.user
app_path = os.path.join(MEDIA_ROOT,"apps")
icon_path = os.path.join(MEDIA_ROOT,"icons")
app_full_path = os.path.join(app_path,app_file_name)
try:
apiobj = AppInfo(app_full_path)
appinfo = apiobj.get_app_data()
app_img=apiobj.make_app_png(icon_path,app_file_name.split(".")[0])
bundle_id = appinfo.get("bundle_id")
SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,get_app_type(app_file_name),os.path.getsize(app_full_path))
except Exception as e:
print(e)
delete_apps_storage(app_file_name.split(".")[0], 0)
delete_apps_storage(app_file_name.split(".")[0], 1)
return
def SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,short,size): def SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,short,size):
app_uuid = make_app_uuid(user_obj,bundle_id+app_file_name.split(".")[1]) app_uuid = make_app_uuid(user_obj,bundle_id+app_file_name.split(".")[1])
##判断是否存在该app ##判断是否存在该app
@ -221,7 +148,9 @@ def SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,short,size):
appmobj = Apps.objects.create(**appdata) appmobj = Apps.objects.create(**appdata)
except Exception as e: except Exception as e:
print(e) print(e)
delete_apps_storage(app_file_name.split(".")[0], get_release_type(app_file_name, appinfo)) storage = Storage(user_obj)
storage.delete_file(app_file_name)
storage.delete_file(app_img)
return return
else: else:
try: try:
@ -230,7 +159,6 @@ def SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,short,size):
appmobj.save() appmobj.save()
except Exception as e: except Exception as e:
print(e) print(e)
# appmobj.short = short
appmobj.name = appinfo["labelname"] appmobj.name = appinfo["labelname"]
appmobj.save() appmobj.save()
@ -238,7 +166,7 @@ def SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,short,size):
release_data = { release_data = {
"app_id": appmobj, "app_id": appmobj,
"icon_url": "/".join([MEDIA_URL.strip("/"), "icons", app_img]), "icon_url": app_img,
"release_id": app_file_name.split(".")[0], "release_id": app_file_name.split(".")[0],
"build_version": appinfo.get("version"), "build_version": appinfo.get("version"),
"app_version": appinfo.get("versioncode"), "app_version": appinfo.get("versioncode"),

@ -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)

@ -1,195 +1,11 @@
from rest_framework import serializers from rest_framework import serializers
from api import models from api import models
from api.utils.app.analyze import bytes2human,make_download_token from api.utils.app.apputils import bytes2human
from api.utils.qiniu.tools import QiNiu from api.utils.storage.storage import Storage
import os import os
# class CourseCategorySerializer(serializers.ModelSerializer):
# class Meta:
# model = models.Course
# fields = (
# "id",
# "name",
# )
#
# class CourseSerializer(serializers.ModelSerializer):
# level = serializers.CharField(source="get_level_display")
# coursedetail_id=serializers.CharField(source="coursedetail.pk")
# class Meta:
# model = models.Course
# fields = (
# 'id',
# 'name',
# 'course_img',
# 'brief',
# 'level',
# "coursedetail_id"
# )
#
# def to_representation(self, instance):
#
# data = super(CourseSerializer, self).to_representation(instance)
# # 购买人数
# # data["people_buy"] = instance.order_details.all().count()
# # 价格套餐列表
# price_policies = instance.price_policy.all().order_by("price").only("price")
#
# price = getattr(price_policies.first(), "price", 0)
#
# if price_policies and price == 0:
# is_free = True
# price = "免费"
# origin_price = "原价¥{}".format(price_policies.last().price)
# else:
# is_free = False
# price = "¥{}".format(price)
# origin_price = None
#
# # 是否免费
# data["is_free"] = is_free
# # 展示价格
# data["price"] = price
# # 原价
# data["origin_price"] = origin_price
#
# return data
#
#
# class CourseDetailSerializer(serializers.ModelSerializer):
#
# name=serializers.CharField(source="course.name")
# prices = serializers.SerializerMethodField()
# brief = serializers.StringRelatedField(source='course.brief')
# study_all_time = serializers.StringRelatedField(source='hours')
# level = serializers.CharField(source='course.get_level_display')
#
# teachers_info = serializers.SerializerMethodField()
# is_online = serializers.SerializerMethodField()
# recommend_coursesinfo = serializers.SerializerMethodField()
# # learnnumber = serializers.SerializerMethodField()
# # OftenAskedQuestion = serializers.SerializerMethodField()
#
# class Meta:
# model = models.CourseDetail
# fields="__all__"
#
# def get_prices(self, obj):
# return PricePolicySerializer(
# obj.course.price_policy.all(), many=True, context=self.context
# ).data
#
# def get_study_all_time(self, obj):
# return "30小时"
#
#
# def get_recommend_coursesinfo(self, obj):
# courses = RecommendCourseSerializer(obj.recommend_courses.all(), many=True)
# return courses.data
#
# def get_teachers_info(self, obj):
# teachers = TeacherSerializer(obj.teachers.all(), many=True)
# return teachers.data
#
# def get_is_online(self, obj):
# if obj.course.status == 0:
# return True
# elif obj.course.status == 2:
# return False
# else:
# return ''
#
# # def get_learnnumber(self, obj):
# # return obj.course.order_details.all().count()
#
#
# # def get_OftenAskedQuestion(self, obj):
# # question_queryset = models.OftenAskedQuestion.objects.filter(content_type__model='Course',
# # object_id=obj.course.id)
# # serializer = OftenAskedQuestionSerializer(question_queryset, many=True)
# # return serializer.data
# #
#
#
# class RecommendCourseSerializer(serializers.ModelSerializer):
#
# course_id = serializers.CharField(source="pk")
# course_name = serializers.CharField(source="name")
#
# class Meta:
# model = models.Course
# fields = ('course_id', 'course_name',)
#
#
# class PricePolicySerializer(serializers.ModelSerializer):
# valid_period_name = serializers.StringRelatedField(source="get_valid_period_display")
#
# class Meta:
# model = models.PricePolicy
# fields = ('id', 'valid_period', 'valid_period_name', 'price',)
#
#
# class CourseChapterSerializer(serializers.ModelSerializer):
# chapter_name = serializers.SerializerMethodField()
# chapter_symbol = serializers.SerializerMethodField()
#
# class Meta:
# model = models.CourseChapter
# fields = (
# 'id',
# 'chapter_name',
# 'chapter_symbol',
# )
#
# def get_chapter_name(self, instance):
# return '第%s章·%s' % (instance.chapter, instance.name)
#
# def get_chapter_symbol(self, instance):
# return "chapter_%s_%s" % (self.context.get('enrolled_course_id', 1), instance.id)
#
# def to_representation(self, instance):
#
# data = super(CourseChapterSerializer, self).to_representation(instance)
#
# queryset = instance.coursesections.all().order_by("order")
# # 获取章节对应的课时数量
# data["section_of_count"] = queryset.count()
# data["free_trail"] = queryset.filter(free_trail=True).exists()
# data["coursesections"] = SectionSerializer(
# queryset, many=True, read_only=True, context=self.context
# ).data
#
# return data
#
#
# class OftenAskedQuestionSerializer(serializers.ModelSerializer):
# question_tittle = serializers.SerializerMethodField()
# question_answer = serializers.SerializerMethodField()
#
# class Meta:
# model = models.OftenAskedQuestion
# fields = ('question_tittle', 'question_answer')
#
# def get_question_tittle(self, obj):
# return obj.question
#
# def get_question_answer(self, obj):
# return obj.answer
#
#
# class TeacherSerializer(serializers.ModelSerializer):
#
# teacher_id = serializers.CharField(source="pk")
# teacher_name = serializers.CharField(source="name")
# teacher_brief = serializers.CharField(source="brief")
# teacher_image = serializers.CharField(source="image")
#
#
# class Meta:
# model = models.Teacher
# fields = ('teacher_id', 'teacher_name', 'title', 'signature', 'teacher_image', 'teacher_brief')
#
#
class UserInfoSerializer(serializers.ModelSerializer): class UserInfoSerializer(serializers.ModelSerializer):
# gender = serializers.SerializerMethodField(source="get_gender_display") # gender = serializers.SerializerMethodField(source="get_gender_display")
@ -230,8 +46,10 @@ class AppsSerializer(serializers.ModelSerializer):
if master_release_obj: if master_release_obj:
icon_url = "/".join([obj.user_id.domain_name, master_release_obj.icon_url]) icon_url = "/".join([obj.user_id.domain_name, master_release_obj.icon_url])
qiniu = QiNiu()
icon_url = qiniu.get_qiniu_download_token( os.path.basename(master_release_obj.icon_url)) if self.context.get("storage", None) and self.context.get("storage") != "undefined":
storage = self.context.get("storage", None)
icon_url = storage.get_download_url(os.path.basename(master_release_obj.icon_url))
datainfo = { datainfo = {
"app_version": master_release_obj.app_version, "app_version": master_release_obj.app_version,
"icon_url": icon_url, "icon_url": icon_url,
@ -252,7 +70,9 @@ class AppsSerializer(serializers.ModelSerializer):
apptype='.apk' apptype='.apk'
else: else:
apptype = '.ipa' apptype = '.ipa'
download_url = qiniu.get_qiniu_download_token(master_release_obj.release_id+apptype) if self.context.get("storage", None) and self.context.get("storage") != "undefined":
storage = self.context.get("storage", None)
download_url = storage.get_download_url(master_release_obj.release_id+apptype)
datainfo["download_url"] = download_url datainfo["download_url"] = download_url
return datainfo return datainfo
@ -278,26 +98,31 @@ class AppReleaseSerializer(serializers.ModelSerializer):
master_color = serializers.SerializerMethodField() master_color = serializers.SerializerMethodField()
icon_url = serializers.SerializerMethodField() icon_url = serializers.SerializerMethodField()
binary_size= serializers.SerializerMethodField() binary_size= serializers.SerializerMethodField()
def get_binary_size(self,obj): def get_binary_size(self,obj):
return bytes2human(obj.binary_size) return bytes2human(obj.binary_size)
def get_download_url(self,obj): def get_download_url(self,obj):
download_url = "/".join([obj.app_id.user_id.domain_name, "download", obj.release_id]) # download_url = "/".join([obj.app_id.user_id.domain_name, "download", obj.release_id])
download_url = download_url + "?token=" + make_download_token(obj.release_id,300) # download_url = download_url + "?token=" + make_download_token(obj.release_id,300)
download_url=''
if obj.release_type == 0: if obj.release_type == 0:
apptype = '.apk' apptype = '.apk'
else: else:
apptype = '.ipa' apptype = '.ipa'
qiniu = QiNiu() if self.context.get("storage", None) and self.context.get("storage") != "undefined":
download_url = qiniu.get_qiniu_download_token(obj.release_id + apptype) storage = self.context.get("storage", None)
download_url = storage.get_download_url(obj.release_id + apptype)
return download_url return download_url
def get_icon_url(self, obj): def get_icon_url(self, obj):
icon_url="/".join([obj.app_id.user_id.domain_name, obj.icon_url]) icon_url="/".join([obj.app_id.user_id.domain_name, obj.icon_url])
qiniu = QiNiu() if self.context.get("storage", None) and self.context.get("storage") != "undefined":
icon_url = qiniu.get_qiniu_download_token(os.path.basename(obj.icon_url)) storage = self.context.get("storage", None)
icon_url = storage.get_download_url(os.path.basename(obj.icon_url))
return icon_url return icon_url

@ -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

@ -3,19 +3,21 @@
# project: 3月 # project: 3月
# author: liuyu # author: liuyu
# date: 2020/3/18 # date: 2020/3/18
'''
主要是调用七牛云存储处理七牛云存储的逻辑
'''
from qiniu import Auth from qiniu import Auth
from qiniu import BucketManager from qiniu import BucketManager
class QiNiu(object): class QiNiuOss(object):
def __init__(self): def __init__(self,access_key,secret_key,bucket_name,domain_name):
access_key="mTqfvkLVSTVb2_1ERjDlFS_WAHLSkpDxYr4e4fiJ" access_key=access_key or "mTqfvkLVSTVb2_1ERjDlFS_WAHLSkpDxYr4e4fiJ"
secret_key="0G9fXfgmi8h1-bmEsABYkE6apf8IuwKpj3hYLynv" secret_key=secret_key or "0G9fXfgmi8h1-bmEsABYkE6apf8IuwKpj3hYLynv"
self.bucket_name = 'fir-storage' self.bucket_name = bucket_name or 'fir-storage'
self.download_domain = 'fly-cdn.dvcloud.xin' self.domain_name = domain_name or 'fly-cdn.dvcloud.xin'
self.qiniu_obj = Auth(access_key,secret_key) self.qiniu_obj = Auth(access_key,secret_key)
def get_qiniu_upload_token(self,name): def get_upload_token(self,name,expires=1800):
# 生成上传 Token,可以指定过期时间等 # 生成上传 Token,可以指定过期时间等
# 上传策略示例 # 上传策略示例
# https://developer.qiniu.com/kodo/manual/1206/put-policy # https://developer.qiniu.com/kodo/manual/1206/put-policy
@ -25,23 +27,22 @@ class QiNiu(object):
# 'persistentOps':'imageView2/1/w/200/h/200' # 'persistentOps':'imageView2/1/w/200/h/200'
} }
# 3600为token过期时间,秒为单位。3600等于一小时 # 3600为token过期时间,秒为单位。3600等于一小时
token = self.qiniu_obj.upload_token(self.bucket_name, name, 3600, policy) token = self.qiniu_obj.upload_token(self.bucket_name, name, expires, policy)
# print(token) # print(token)
return token return token
def get_qiniu_download_token(self,name): def get_download_url(self,name,expires=1800):
#有两种方式构造base_url的形式 #有两种方式构造base_url的形式
base_url = 'http://%s/%s' % (self.download_domain, name) base_url = '%s/%s' % (self.domain_name, name)
#或者直接输入url的方式下载 #或者直接输入url的方式下载
#可以设置token过期时间 #可以设置token过期时间
private_url = self.qiniu_obj.private_download_url(base_url, expires=3600) private_url = self.qiniu_obj.private_download_url(base_url, expires=expires)
print(private_url) print(private_url)
return private_url return private_url
def del_qiniu_file(self,name): def del_file(self,name):
# 初始化BucketManager # 初始化BucketManager
bucket = BucketManager(self.qiniu_obj) bucket = BucketManager(self.qiniu_obj)
# 你要测试的空间, 并且这个key在你空间中存在
# 删除bucket_name 中的文件 key # 删除bucket_name 中的文件 key
ret, info = bucket.delete(self.bucket_name, name) ret, info = bucket.delete(self.bucket_name, name)
print(info) print(info)

@ -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": ""
}
'''

@ -8,18 +8,19 @@ from rest_framework.views import APIView
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.auth import ExpiringTokenAuthentication from api.utils.auth import ExpiringTokenAuthentication
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet from django.db.models import Sum
from django.db.models import Sum,aggregates
import os import os
from fir_ser import settings from fir_ser import settings
from api.utils.randomstrings import make_from_user_uuid from api.utils.app.randomstrings import make_from_user_uuid
from api.utils.storage.storage import Storage
import redis from api.models import Apps, AppReleaseInfo
from api.models import Apps,UserInfo,AppReleaseInfo from api.utils.serializer import AppsSerializer, AppReleaseSerializer, UserInfoSerializer
from api.utils.serializer import AppsSerializer,AppReleaseSerializer,UserInfoSerializer from api.utils.app.apputils import delete_apps_icon_storage
from api.utils.app.analyze import delete_apps_storage,delete_apps_icon_storage from rest_framework.pagination import PageNumberPagination
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination
REDIS_CONN = redis.Redis(decode_responses=True)
# REDIS_CONN = redis.Redis(decode_responses=True)
class AppsPageNumber(PageNumberPagination): class AppsPageNumber(PageNumberPagination):
@ -29,9 +30,7 @@ class AppsPageNumber(PageNumberPagination):
max_page_size = None # 最大页码数限制 max_page_size = None # 最大页码数限制
class AppsView(APIView): class AppsView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ] authentication_classes = [ExpiringTokenAuthentication, ]
def get(self, request): def get(self, request):
@ -40,107 +39,107 @@ class AppsView(APIView):
act_type = request.query_params.get("act", None) act_type = request.query_params.get("act", None)
res = BaseResponse() res = BaseResponse()
res.hdata={"all_hits_count":0} res.hdata = {"all_hits_count": 0}
res.hdata["upload_domain"] = request.user.domain_name res.hdata["upload_domain"] = request.user.domain_name
res.hdata["ios_count"] = Apps.objects.filter(type=1,user_id=request.user).values('app_id').count() res.hdata["ios_count"] = Apps.objects.filter(type=1, user_id=request.user).values('app_id').count()
res.hdata["android_count"] = Apps.objects.filter(type=0,user_id=request.user).values('app_id').count() res.hdata["android_count"] = Apps.objects.filter(type=0, user_id=request.user).values('app_id').count()
all_hits_obj = Apps.objects.filter(user_id=request.user).aggregate(count_hits=Sum('count_hits')) all_hits_obj = Apps.objects.filter(user_id=request.user).aggregate(count_hits=Sum('count_hits'))
if all_hits_obj : if all_hits_obj:
count_hits=all_hits_obj.get("count_hits",0) count_hits = all_hits_obj.get("count_hits", 0)
if count_hits: if count_hits:
if count_hits > 0: if count_hits > 0:
request.user.all_download_times +=count_hits request.user.all_download_times += count_hits
request.user.save() request.user.save()
else: else:
count_hits = 0 count_hits = 0
res.hdata["all_hits_count"]=count_hits res.hdata["all_hits_count"] = count_hits
else: else:
res.hdata["all_hits_count"]=0 res.hdata["all_hits_count"] = 0
res.hdata["android_today_hits_count"] = 0 res.hdata["android_today_hits_count"] = 0
res.hdata["ios_today_hits_count"] = 0 res.hdata["ios_today_hits_count"] = 0
if app_type == "android": if app_type == "android":
filter_data={"user_id":request.user,"type":0} filter_data = {"user_id": request.user, "type": 0}
elif app_type == "ios": elif app_type == "ios":
filter_data={"user_id":request.user,"type":1} filter_data = {"user_id": request.user, "type": 1}
else: else:
filter_data={"user_id":request.user} filter_data = {"user_id": request.user}
if act_type == "combo": if act_type == "combo":
filter_data["has_combo"]=None filter_data["has_combo"] = None
apps_obj = Apps.objects.filter(**filter_data) apps_obj = Apps.objects.filter(**filter_data)
page_obj = AppsPageNumber() page_obj = AppsPageNumber()
app_page_serializer = page_obj.paginate_queryset(queryset=apps_obj.order_by("-updated_time"), request=request, view=self) app_page_serializer = page_obj.paginate_queryset(queryset=apps_obj.order_by("-updated_time"), request=request,
view=self)
# app_serializer = AppsSerializer(apps_obj.order_by("-updated_time"),many=True) app_serializer = AppsSerializer(app_page_serializer, many=True, context={"storage": Storage(request.user)})
app_serializer = AppsSerializer(app_page_serializer,many=True)
userserializer = UserInfoSerializer(request.user) userserializer = UserInfoSerializer(request.user)
res.userinfo={} res.userinfo = {}
res.has_next={} res.has_next = {}
res.userinfo = userserializer.data res.userinfo = userserializer.data
res.data = app_serializer.data res.data = app_serializer.data
res.has_next = page_obj.page.has_next() res.has_next = page_obj.page.has_next()
return Response(res.dict) return Response(res.dict)
class AppInfoView(APIView):
class AppinfoView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ] authentication_classes = [ExpiringTokenAuthentication, ]
def get(self, request,app_id):
def get(self, request, app_id):
res = BaseResponse() res = BaseResponse()
if app_id: if app_id:
apps_obj = Apps.objects.filter(user_id=request.user,app_id=app_id).first() apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
if apps_obj: if apps_obj:
app_serializer = AppsSerializer(apps_obj) app_serializer = AppsSerializer(apps_obj, context={"storage": Storage(request.user)})
res.data = app_serializer.data res.data = app_serializer.data
else: else:
res.msg="未找到该应用" res.msg = "未找到该应用"
res.code=1003 res.code = 1003
userserializer = UserInfoSerializer(request.user) userserializer = UserInfoSerializer(request.user)
res.userinfo = {} res.userinfo = {}
res.userinfo = userserializer.data res.userinfo = userserializer.data
return Response(res.dict) return Response(res.dict)
def delete(self,request,app_id):
def delete(self, request, app_id):
res = BaseResponse() res = BaseResponse()
if app_id: if app_id:
apps_obj = Apps.objects.filter(user_id=request.user,app_id=app_id).first() apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
if apps_obj: if apps_obj:
storage = Storage(request.user)
for appreleaseobj in AppReleaseInfo.objects.filter(app_id=apps_obj).all(): for appreleaseobj in AppReleaseInfo.objects.filter(app_id=apps_obj).all():
delete_apps_storage(appreleaseobj.release_id,appreleaseobj.release_type) storage.delete_file(appreleaseobj.release_id, appreleaseobj.release_type)
storage.delete_file(appreleaseobj.icon_url)
appreleaseobj.delete() appreleaseobj.delete()
has_combo = apps_obj.has_combo has_combo = apps_obj.has_combo
if has_combo: if has_combo:
apps_obj.has_combo.has_combo=None apps_obj.has_combo.has_combo = None
apps_obj.delete() apps_obj.delete()
return Response(res.dict) return Response(res.dict)
def put(self,request,app_id): def put(self, request, app_id):
res = BaseResponse() res = BaseResponse()
if app_id: if app_id:
data = request.data data = request.data
has_combo = data.get("has_combo",None) has_combo = data.get("has_combo", None)
if has_combo: if has_combo:
actions = has_combo.get("action",None) actions = has_combo.get("action", None)
hcombo_id = has_combo.get("hcombo_id",None) hcombo_id = has_combo.get("hcombo_id", None)
if actions and hcombo_id: if actions and hcombo_id:
has_combo = Apps.objects.filter(user_id=request.user,app_id=hcombo_id) has_combo = Apps.objects.filter(user_id=request.user, app_id=hcombo_id)
apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id) apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id)
try: try:
if actions == "UNCOMBO": if actions == "UNCOMBO":
if apps_obj.filter(has_combo=has_combo.first()).first(): if apps_obj.filter(has_combo=has_combo.first()).first():
apps_obj.update(**{"has_combo":None}) apps_obj.update(**{"has_combo": None})
has_combo.update(**{"has_combo":None}) has_combo.update(**{"has_combo": None})
elif actions == "COMBO": elif actions == "COMBO":
apps_obj.update(**{"has_combo": has_combo.first()}) apps_obj.update(**{"has_combo": has_combo.first()})
@ -153,9 +152,9 @@ class AppinfoView(APIView):
else: else:
try: try:
apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first() apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
apps_obj.description= data.get("description",apps_obj.description) apps_obj.description = data.get("description", apps_obj.description)
apps_obj.short= data.get("short",apps_obj.short) apps_obj.short = data.get("short", apps_obj.short)
apps_obj.name= data.get("name",apps_obj.name) apps_obj.name = data.get("name", apps_obj.name)
apps_obj.save() apps_obj.save()
except Exception as e: except Exception as e:
res.code = 1005 res.code = 1005
@ -163,8 +162,7 @@ class AppinfoView(APIView):
return Response(res.dict) return Response(res.dict)
def post(self, request, app_id):
def post(self, request,app_id):
res = BaseResponse() res = BaseResponse()
# 获取多个file # 获取多个file
@ -173,8 +171,8 @@ class AppinfoView(APIView):
# 将文件缓存到本地后上传 # 将文件缓存到本地后上传
try: try:
app_type = file_obj.name.split(".")[-1] app_type = file_obj.name.split(".")[-1]
if app_type in ['png','jpeg','jpg']: if app_type in ['png', 'jpeg', 'jpg']:
#上传图片 # 上传图片
pass pass
else: else:
raise raise
@ -186,16 +184,13 @@ class AppinfoView(APIView):
if app_id: if app_id:
apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first() apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
if apps_obj: if apps_obj:
release_obj = AppReleaseInfo.objects.filter(app_id = apps_obj,is_master=True).first() release_obj = AppReleaseInfo.objects.filter(app_id=apps_obj, is_master=True).first()
if release_obj: if release_obj:
random_file_name = make_from_user_uuid(request.user) random_file_name = make_from_user_uuid(request.user)
old_file_name = os.path.basename(release_obj.icon_url) old_file_name = os.path.basename(release_obj.icon_url)
local_file = os.path.join(settings.MEDIA_ROOT, "icons",
local_file = os.path.join(settings.MEDIA_ROOT,"icons",random_file_name+ "." + old_file_name.split(".")[1]) random_file_name + "." + old_file_name.split(".")[1])
# 读取传入的文件 # 读取传入的文件
try: try:
destination = open(local_file, 'wb+') destination = open(local_file, 'wb+')
@ -207,9 +202,11 @@ class AppinfoView(APIView):
res.code = 1003 res.code = 1003
res.msg = "数据写入失败" res.msg = "数据写入失败"
return Response(res.dict) return Response(res.dict)
release_obj.icon_url = release_obj.icon_url.replace(old_file_name.split(".")[0],random_file_name) release_obj.icon_url = release_obj.icon_url.replace(old_file_name.split(".")[0],
random_file_name)
release_obj.save() release_obj.save()
delete_apps_icon_storage(old_file_name,'icons')
delete_apps_icon_storage(old_file_name, 'icons')
else: else:
res.code = 1003 res.code = 1003
@ -219,89 +216,92 @@ class AppinfoView(APIView):
class AppReleaseinfoView(APIView): class AppReleaseinfoView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ] authentication_classes = [ExpiringTokenAuthentication, ]
def get(self, request,app_id,act): def get(self, request, app_id, act):
res = BaseResponse() res = BaseResponse()
res.data={} res.data = {}
if app_id: if app_id:
apps_obj = Apps.objects.filter(user_id=request.user,app_id=app_id).first() apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
if apps_obj: if apps_obj:
app_serializer = AppsSerializer(apps_obj) app_serializer = AppsSerializer(apps_obj, context={"storage": Storage(request.user)})
res.data["currentapp"] = app_serializer.data res.data["currentapp"] = app_serializer.data
app_release_obj = AppReleaseInfo.objects.filter(app_id=apps_obj).all().order_by("-created_time") app_release_obj = AppReleaseInfo.objects.filter(app_id=apps_obj).all().order_by("-created_time")
app_release_serializer = AppReleaseSerializer(app_release_obj,many=True) app_release_serializer = AppReleaseSerializer(app_release_obj, many=True,
context={"storage": Storage(request.user)})
res.data["release_apps"] = app_release_serializer.data res.data["release_apps"] = app_release_serializer.data
else: else:
res.msg="未找到该应用" res.msg = "未找到该应用"
res.code=1003 res.code = 1003
return Response(res.dict) return Response(res.dict)
def delete(self,request,app_id,act): def delete(self, request, app_id, act):
res = BaseResponse() res = BaseResponse()
if app_id: if app_id:
apps_obj = Apps.objects.filter(user_id=request.user,app_id=app_id).first() apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
if apps_obj: if apps_obj:
storage = Storage(request.user)
apprelease_count = AppReleaseInfo.objects.filter(app_id=apps_obj).values("release_id").count() apprelease_count = AppReleaseInfo.objects.filter(app_id=apps_obj).values("release_id").count()
appreleaseobj =AppReleaseInfo.objects.filter(app_id=apps_obj,release_id=act).first() appreleaseobj = AppReleaseInfo.objects.filter(app_id=apps_obj, release_id=act).first()
if not appreleaseobj.is_master : if not appreleaseobj.is_master:
delete_apps_storage(appreleaseobj.release_id,appreleaseobj.release_type) storage.delete_file(appreleaseobj.release_id, appreleaseobj.release_type)
storage.delete_file(appreleaseobj.icon_url)
appreleaseobj.delete() appreleaseobj.delete()
elif appreleaseobj.is_master and apprelease_count <2: elif appreleaseobj.is_master and apprelease_count < 2:
delete_apps_storage(appreleaseobj.release_id,appreleaseobj.release_type) storage.delete_file(appreleaseobj.release_id, appreleaseobj.release_type)
storage.delete_file(appreleaseobj.icon_url)
appreleaseobj.delete() appreleaseobj.delete()
has_combo = apps_obj.has_combo has_combo = apps_obj.has_combo
if has_combo: if has_combo:
apps_obj.has_combo.has_combo=None apps_obj.has_combo.has_combo = None
apps_obj.delete() apps_obj.delete()
else: else:
pass pass
return Response(res.dict) return Response(res.dict)
def put(self,request,app_id,act): def put(self, request, app_id, act):
res = BaseResponse() res = BaseResponse()
res.data={} res.data = {}
if app_id: if app_id:
apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first() apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
if apps_obj: if apps_obj:
appreleaseobj = AppReleaseInfo.objects.filter(app_id=apps_obj, release_id=act) appreleaseobj = AppReleaseInfo.objects.filter(app_id=apps_obj, release_id=act)
make_master = request.data.get("make_master",None) make_master = request.data.get("make_master", None)
try: try:
if make_master and make_master == act: if make_master and make_master == act:
AppReleaseInfo.objects.filter(app_id=apps_obj).update(**{"is_master":False}) AppReleaseInfo.objects.filter(app_id=apps_obj).update(**{"is_master": False})
appreleaseobj.update(**{"is_master":True}) appreleaseobj.update(**{"is_master": True})
else: else:
appreleaseobj.update(**{"changelog":request.data.get("changelog",appreleaseobj.first().changelog)}) appreleaseobj.update(
binary_url = request.data.get("binary_url",None) **{"changelog": request.data.get("changelog", appreleaseobj.first().changelog)})
if binary_url !='': binary_url = request.data.get("binary_url", None)
if binary_url != '':
if binary_url: if binary_url:
if not binary_url.startswith('http'): if not binary_url.startswith('http'):
binary_url='http://%s'%binary_url binary_url = 'http://%s' % binary_url
else: else:
binary_url =appreleaseobj.first().binary_url binary_url = appreleaseobj.first().binary_url
print(binary_url) print(binary_url)
appreleaseobj.update(**{"binary_url":binary_url}) appreleaseobj.update(**{"binary_url": binary_url})
except Exception as e: except Exception as e:
res.code = 1006 res.code = 1006
res.msg = "更新失败" res.msg = "更新失败"
return Response(res.dict) return Response(res.dict)
app_serializer = AppsSerializer(apps_obj) app_serializer = AppsSerializer(apps_obj)
res.data["currentapp"] = app_serializer.data res.data["currentapp"] = app_serializer.data
app_release_obj = AppReleaseInfo.objects.filter(app_id=apps_obj).all().order_by("-created_time") app_release_obj = AppReleaseInfo.objects.filter(app_id=apps_obj).all().order_by("-created_time")
app_release_serializer = AppReleaseSerializer(app_release_obj,many=True) app_release_serializer = AppReleaseSerializer(app_release_obj, many=True,
context={"storage": Storage(request.user)})
res.data["release_apps"] = app_release_serializer.data res.data["release_apps"] = app_release_serializer.data
return Response(res.dict) return Response(res.dict)

@ -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})

@ -5,19 +5,14 @@
# date: 2020/3/6 # date: 2020/3/6
from rest_framework.views import APIView from rest_framework.views import APIView
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.auth import ExpiringTokenAuthentication
from django.http.multipartparser import MultiPartParser
from api.utils.randomstrings import make_from_user_uuid
from rest_framework.response import Response from rest_framework.response import Response
from fir_ser import settings from fir_ser import settings
from api.utils.app.analyze import make_resigned from api.utils.TokenManager import DownloadToken
from api.utils.app.TokenManager import DownloadToken
import os import os
from api.utils.randomstrings import make_random_uuid
from api.utils.serializer import AppsSerializer from api.utils.serializer import AppsSerializer
from api.models import Apps,AppReleaseInfo from api.models import Apps,AppReleaseInfo
from django.http import HttpResponse, Http404, FileResponse from django.http import FileResponse
class DownloadView(APIView): class DownloadView(APIView):
''' '''
文件下载接口 文件下载接口
@ -25,11 +20,11 @@ class DownloadView(APIView):
# authentication_classes = [ExpiringTokenAuthentication, ] # authentication_classes = [ExpiringTokenAuthentication, ]
# parser_classes = (MultiPartParser,) # parser_classes = (MultiPartParser,)
def get(self,request,release_id): def get(self,request,filename):
# release_id = release_id.split(".")[1] # release_id = release_id.split(".")[1]
res = BaseResponse() res = BaseResponse()
print(filename)
downtoken = request.query_params.get("token", None) downtoken = request.query_params.get("token", None)
iostype = request.query_params.get("type",None) iostype = request.query_params.get("type",None)
if not downtoken: if not downtoken:
@ -38,55 +33,70 @@ class DownloadView(APIView):
return Response(res.dict) return Response(res.dict)
dtoken = DownloadToken() dtoken = DownloadToken()
if dtoken.verify_token(downtoken,release_id): if dtoken.verify_token(downtoken,filename):
#校验成功,可以下载数据 file_path = os.path.join(settings.MEDIA_ROOT, filename)
apprelease_obj = AppReleaseInfo.objects.filter(release_id=release_id).first() try:
if apprelease_obj: response = FileResponse(open(file_path, 'rb'))
try: except Exception as e:
app_type="ipa" print(e)
if apprelease_obj.release_type == 0: response = FileResponse()
app_type = "apk" response['content_type'] = "application/octet-stream"
response['Content-Disposition'] = 'attachment; filename=' + filename
if iostype == "resigned.plist": return response
file_path=make_random_uuid()
downtoken=dtoken.make_token([release_id])
domain_name = apprelease_obj.app_id.user_id.domain_name
if not domain_name:
ser_url = request.META.get("HTTP_HOST", "/")
SERVER_PROTOCOL = request.META.get("SERVER_PROTOCOL", "http/1.1")
ser_protocol = SERVER_PROTOCOL.split("/")[0].lower()
domain_name = "%s://%s" % (ser_protocol, ser_url)
bin_url = domain_name+"/download/"+\
release_id+"?token="+\
downtoken
img_url=apprelease_obj.icon_url
bundle_id = apprelease_obj.app_id.bundle_id
bundle_version = apprelease_obj.build_version
name = apprelease_obj.app_id.name
ios_plist_bytes = make_resigned(bin_url,img_url,bundle_id,bundle_version,name)
response = FileResponse(ios_plist_bytes)
response['content_type'] = "text/xml"
else:
apprelease_obj.app_id.count_hits+=1
apprelease_obj.app_id.save()
file_path = os.path.join(settings.MEDIA_ROOT,"apps","%s"%(release_id+"."+app_type))
response = FileResponse(open(file_path, 'rb'))
response['content_type'] = "application/octet-stream"
response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(file_path)
return response
except Exception:
raise Http404
pass
else: else:
res.code=1004 res.code=1004
res.msg="token校验失败" res.msg="token校验失败"
return Response(res.dict) return Response(res.dict)
# #校验成功,可以下载数据
# apprelease_obj = AppReleaseInfo.objects.filter(release_id=release_id).first()
# if apprelease_obj:
# try:
# app_type="ipa"
# if apprelease_obj.release_type == 0:
# app_type = "apk"
#
# if iostype == "resigned.plist":
# file_path=make_random_uuid()
#
# downtoken=dtoken.make_token([release_id])
# domain_name = apprelease_obj.app_id.user_id.domain_name
# if not domain_name:
# ser_url = request.META.get("HTTP_HOST", "/")
# SERVER_PROTOCOL = request.META.get("SERVER_PROTOCOL", "http/1.1")
# ser_protocol = SERVER_PROTOCOL.split("/")[0].lower()
# domain_name = "%s://%s" % (ser_protocol, ser_url)
# bin_url = domain_name+"/download/"+\
# release_id+"?token="+\
# downtoken
# img_url=apprelease_obj.icon_url
# bundle_id = apprelease_obj.app_id.bundle_id
# bundle_version = apprelease_obj.build_version
# name = apprelease_obj.app_id.name
# ios_plist_bytes = make_resigned(bin_url,img_url,bundle_id,bundle_version,name)
# response = FileResponse(ios_plist_bytes)
# response['content_type'] = "text/xml"
#
#
# else:
#
# apprelease_obj.app_id.count_hits+=1
# apprelease_obj.app_id.save()
#
# file_path = os.path.join(settings.MEDIA_ROOT,"apps","%s"%(release_id+"."+app_type))
# response = FileResponse(open(file_path, 'rb'))
# response['content_type'] = "application/octet-stream"
# response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(file_path)
# return response
# except Exception:
# raise Http404
# pass
# else:
# res.code=1004
# res.msg="token校验失败"
# return Response(res.dict)
class DownloadTokenView(APIView): class DownloadTokenView(APIView):
''' '''
获取下载的token信息 获取下载的token信息

@ -6,12 +6,11 @@
from rest_framework.views import APIView from rest_framework.views import APIView
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.auth import ExpiringTokenAuthentication from api.utils.auth import ExpiringTokenAuthentication
from django.http.multipartparser import MultiPartParser from api.utils.app.randomstrings import make_from_user_uuid
from api.utils.randomstrings import make_from_user_uuid
from rest_framework.response import Response from rest_framework.response import Response
from fir_ser import settings from fir_ser import settings
from api.utils.app.analyze import AnalyzeUtil from api.utils.TokenManager import DownloadToken
import os import os,json
class UploadView(APIView): class UploadView(APIView):
''' '''
@ -25,39 +24,61 @@ class UploadView(APIView):
# 获取多个file # 获取多个file
files = request.FILES.getlist('file', None) files = request.FILES.getlist('file', None)
if not files: certinfo = request.data.get('certinfo', None)
res.msg="文件不存在"
for file_obj in files:
# 将文件缓存到本地后上传
try:
app_type = file_obj.name.split(".")[-1]
if app_type != "apk" and app_type != "ipa" :
raise
except Exception as e:
res.code = 1003
res.msg = "错误的类型"
return Response(res.dict)
random_file_name = make_from_user_uuid(request.user) try:
local_file = os.path.join(settings.MEDIA_ROOT,"apps","%s"%(random_file_name+"."+app_type)) certinfo = json.loads(certinfo)
# 读取传入的文件 if not certinfo:
try: res.msg = "token 校验失败"
destination = open(local_file, 'wb+') res.code = 1006
for chunk in file_obj.chunks():
# 写入本地文件
destination.write(chunk)
destination.close()
except Exception as e:
res.code = 1003
res.msg = "数据写入失败"
return Response(res.dict) return Response(res.dict)
except Exception as e:
print(e)
res.msg = "token 校验失败"
res.code = 1006
return Response(res.dict)
try: token_obj = DownloadToken()
AnalyzeUtil("%s"%(random_file_name+"."+app_type),request) if token_obj.verify_token(token=certinfo.get("upload_token",None),release_id=certinfo.get("upload_key",None)):
except Exception as e:
res.code = 1003 if not files:
res.msg = "应用解析失败" res.msg="文件不存在"
return Response(res.dict) for file_obj in files:
# 将文件缓存到本地后上传
print(file_obj.name)
try:
app_type = file_obj.name.split(".")[-1]
if app_type != "apk" and app_type != "ipa" and app_type != 'png' :
raise TypeError
except Exception as e:
print(e)
res.code = 1003
res.msg = "错误的类型"
return Response(res.dict)
random_file_name = make_from_user_uuid(request.user)
# random_file_name = certinfo.get("upload_key",None)
local_file = os.path.join(settings.MEDIA_ROOT,certinfo.get("upload_key",random_file_name))
# 读取传入的文件
try:
destination = open(local_file, 'wb+')
for chunk in file_obj.chunks():
destination.write(chunk)
destination.close()
except Exception as e:
res.code = 1003
res.msg = "数据写入失败"
return Response(res.dict)
# try:
# AnalyzeUtil("%s"%(random_file_name+"."+app_type),request)
# except Exception as e:
# res.code = 1003
# res.msg = "应用解析失败"
# return Response(res.dict)
else:
res.msg = "token 校验失败"
res.code = 1006
return Response(res.dict) return Response(res.dict)

@ -1,21 +1,18 @@
from django.contrib import auth from django.contrib import auth
from api.models import Token, UserInfo from api.models import Token, UserInfo
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ViewSetMixin
from api.utils.serializer import UserInfoSerializer from api.utils.serializer import UserInfoSerializer
from rest_framework.viewsets import generics from api.utils.app.randomstrings import make_from_user_uuid
from api.utils.randomstrings import make_from_user_uuid
from rest_framework.views import APIView from rest_framework.views import APIView
from fir_ser import settings from fir_ser import settings
import binascii import binascii
import os,datetime import os,datetime
from api.utils.app.TokenManager import DownloadToken,generateNumericTokenOfLength from api.utils.TokenManager import DownloadToken,generateNumericTokenOfLength
from api.utils.auth import ExpiringTokenAuthentication from api.utils.auth import ExpiringTokenAuthentication
from api.utils.app.analyze import delete_apps_icon_storage from api.utils.app.apputils import delete_apps_icon_storage
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.captcha_verify import verify
from django.middleware import csrf from django.middleware import csrf
def get_token(request): def get_token(request):

@ -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

@ -6,22 +6,44 @@
from rest_framework.views import APIView from rest_framework.views import APIView
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.auth import ExpiringTokenAuthentication from api.utils.auth import ExpiringTokenAuthentication
from api.utils.randomstrings import make_from_user_uuid from api.utils.app.randomstrings import make_from_user_uuid
from rest_framework.response import Response from rest_framework.response import Response
from fir_ser import settings from api.utils.app.apputils import get_random_short,SaveAppInfos
from api.utils.app.analyze import AnalyzeUtil, get_random_short,SaveAppInfos from api.utils.storage.storage import Storage
import os
from api.utils.qiniu.tools import QiNiu
from api.models import Apps from api.models import Apps
from api.utils.randomstrings import make_app_uuid from api.utils.app.randomstrings import make_app_uuid
class QiNiuUploadView(APIView): class OssUploadView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ] authentication_classes = [ExpiringTokenAuthentication, ]
#
# def get(self,request):
# res = BaseResponse()
# upload_key = request.query_params.get("upload_key", None)
# app_id = request.query_params.get("app_id", None)
# if upload_key:
# if app_id:
# #修改app图片
# app_obj = Apps.objects.filter(app_id=app_id).first()
# if app_obj:
# release_obj = AppReleaseInfo.objects.filter(app_id=app_obj,is_master=True).first()
#
# else:
# res.code = 1005
# res.msg = '该应用找不到'
# pass
# else:
# res.code = 1004
#
#
# return Response(res.dict)
#
def post(self, request): def post(self, request):
res = BaseResponse() res = BaseResponse()
# 1.接受 bundelid ,返回随机应用名称和短连接 # 1.接 bundelid ,返回随机应用名称和短连接
bundleid = request.data.get("bundleid", None) bundleid = request.data.get("bundleid", None)
app_type = request.data.get("type", None) app_type = request.data.get("type", None)
@ -33,7 +55,6 @@ class QiNiuUploadView(APIView):
release_id = make_from_user_uuid(request.user) release_id = make_from_user_uuid(request.user)
png_id = make_from_user_uuid(request.user) png_id = make_from_user_uuid(request.user)
app_obj = Apps.objects.filter(app_id=app_uuid).first() app_obj = Apps.objects.filter(app_id=app_uuid).first()
print(app_obj)
if app_obj: if app_obj:
short = app_obj.short short = app_obj.short
else: else:
@ -43,13 +64,25 @@ class QiNiuUploadView(APIView):
else: else:
upload_key = release_id+'.apk' upload_key = release_id+'.apk'
png_key = png_id+'.png' png_key = png_id+'.png'
qiniu = QiNiu() storage = Storage(request.user)
storage_type = request.user.storage.storage_type
if storage_type == 1:
upload_token = storage.get_upload_token(upload_key)
png_token = storage.get_upload_token(png_key)
elif storage_type == 2:
png_token=upload_token = storage.get_upload_token(upload_key)
else:
upload_token = storage.get_upload_token(upload_key)
png_token = storage.get_upload_token(png_key)
res.data = {"app_uuid": app_uuid, "short": short, res.data = {"app_uuid": app_uuid, "short": short,
"domain_name": request.user.domain_name, "domain_name": request.user.domain_name,
"upload_token":qiniu.get_qiniu_upload_token(upload_key), "upload_token":upload_token,
"upload_key":upload_key, "upload_key":upload_key,
"png_token":qiniu.get_qiniu_upload_token(png_key), "png_token":png_token,
"png_key":png_key} "png_key":png_key,
"storage":storage_type}
else: else:
res.code = 1003 res.code = 1003
@ -57,7 +90,6 @@ class QiNiuUploadView(APIView):
def put(self,request): def put(self,request):
res = BaseResponse() res = BaseResponse()
print(request.data)
data=request.data data=request.data
appinfo={ appinfo={
"labelname":data.get("appname"), "labelname":data.get("appname"),

@ -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>

@ -47,5 +47,5 @@ urlpatterns = [
# re_path("api/v1/trade/alipay/",AlipayTradeView), # re_path("api/v1/trade/alipay/",AlipayTradeView),
# media路径配置 # media路径配置
re_path(r'files/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}), re_path(r'files/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
re_path("download/(?P<release_id>\w+)$", DownloadView.as_view(),name="download"), re_path("download/(?P<filename>\w+\.\w+)$", DownloadView.as_view(),name="download"),
] ]

Loading…
Cancel
Save