存储扩容功能

dependabot/npm_and_yarn/fir_admin/eventsource-1.1.1
nineven 3 years ago
parent 5f967a9b52
commit 3ad5e03504
  1. 8
      fir_client/src/components/apps/FirApps.vue
  2. 163
      fir_client/src/components/user/FirUserStorage.vue
  3. 12
      fir_client/src/restful/index.js
  4. 1
      fir_ser/api/migrations/0001_initial.py
  5. 50
      fir_ser/api/migrations/0002_auto_20220215_2129.py
  6. 23
      fir_ser/api/migrations/0006_auto_20220513_0843.py
  7. 25
      fir_ser/api/models.py
  8. 3
      fir_ser/api/urls.py
  9. 27
      fir_ser/api/utils/modelutils.py
  10. 29
      fir_ser/api/utils/serializer.py
  11. 2
      fir_ser/api/utils/utils.py
  12. 5
      fir_ser/api/views/order.py
  13. 64
      fir_ser/api/views/storage.py
  14. 8
      fir_ser/common/core/sysconfig.py
  15. 44
      fir_ser/common/utils/caches.py
  16. 3
      fir_ser/common/utils/download.py
  17. 5
      fir_ser/config.py

@ -445,8 +445,8 @@
<div>
<el-tooltip placement="top">
<div slot="content">
1.下载应用100M消耗一次下载次数<br>
2.超级签下载下载消耗次数翻倍<br>
1.下载应用 {{ times_info.every_size|diskSize }} 消耗 {{ times_info.base_times / 100 }} 下载次数<br>
2.私有存储模式下载应用 {{ times_info.every_size|diskSize }} 消耗 {{ times_info.private_times / 100 }} 下载次数<br>
</div>
<span class="name">购买次数</span>
</el-tooltip>
@ -1167,6 +1167,10 @@ export default {
getDelappTitle() {
return `删除应用 ${this.delapp.name}`
},
times_info() {
let times_info = this.$store.state.userinfo.times_info
return times_info ? times_info : {}
}
},
filters: {
diskSize,

@ -191,6 +191,37 @@
<el-button @click="shareVisible=false">取消</el-button>
</el-form>
</el-dialog>
<el-dialog :close-on-click-modal="false" :destroy-on-close="true" :visible.sync="exchangeVisible"
style="text-align:center" title="存储扩容" width="800px">
<el-tag style="margin-bottom: 20px" type="warning">请勿多次购买多次购买将按照最大的扩容容量进行计算</el-tag>
<el-form label-width="120px">
<el-form-item label="购买时间" style="width: 100%">
<el-radio-group v-model="exchange_prices_key" size="small">
<el-radio v-for="pay in exchange_prices.pay_times" :key="pay.name" :label="pay.name" border>
{{ pay.title }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="扩容存储大小" style="width: 100%;text-align: left">
<el-row :gutter="24">
<el-col :span="10">
<el-input-number v-model="exchange_number" :max="999999999" :min="10" :step="10">
</el-input-number>
</el-col>
<el-col :span="13">
<el-tag style="margin-left: 30px">扩容存储空间大小 {{ exchange_number }} GB</el-tag>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="所消耗下载次数" style="width: 100%;text-align: left">
<el-tag style="margin-left: 30px"> {{ exchangetimes }}</el-tag>
</el-form-item>
<el-button @click="sureExchangeFun(exchange_number,exchange_prices_key)">确定扩容</el-button>
<el-button @click="exchangeVisible=false">取消</el-button>
</el-form>
</el-dialog>
<el-tabs v-model="activeName" tab-position="top" type="border-card" @tab-click="handleClick">
<el-tab-pane label="存储选择" name="change">
@ -218,7 +249,9 @@
trigger="hover">
<div>
<el-link :underline="false">存储最大容量空间 {{ diskSize(storageinfo.max_storage_capacity) }}</el-link>
<br>
<el-link :underline="false">已经使用容量 {{ diskSize(storageinfo.used_number) }}</el-link>
<br>
<el-link :underline="false">当前可用容量
{{ diskSize(storageinfo.max_storage_capacity - storageinfo.used_number) }}
</el-link>
@ -265,6 +298,11 @@
<el-form-item label="存储最大容量" label-width="110px" style="text-align: left">
<el-tag>存储最大容量空间 {{ diskSize(storageinfo.max_storage_capacity) }}</el-tag>
<span v-if="percentage>=90 && use_storage_id===-1">
<el-tag style="margin: 0 10px" type="danger">容量即将不足请立即扩容</el-tag> <el-button size="small"
@click="exchangeFun">扩容</el-button>
</span>
</el-form-item>
<el-form-item label="已经使用容量" label-width="110px" style="text-align: left">
<el-tag>存储已经使用容量空间 {{ diskSize(storageinfo.used_number) }}
@ -663,6 +701,68 @@
</el-table>
</el-tab-pane>
<el-tab-pane label="默认存储扩容记录" name="exchange">
<div style="text-align: left;margin-bottom: 15px">
<el-tag>该记录为默认存储扩容记录私有存储无需扩容</el-tag>
</div>
<el-table
v-loading="loading"
:data="storage_exchange_lists"
border
stripe
style="width: 100%">
<el-table-column
align="center"
label="兑换存储大小"
prop="storage_size"
width="140">
<template slot-scope="scope">
{{ diskSize(scope.row.storage_size) }}
</template>
</el-table-column>
<el-table-column
align="center"
label="消耗下载次数"
prop="download_times"
width="140">
<template slot-scope="scope">
{{ parseInt(scope.row.download_times / 100) }}
</template>
</el-table-column>
<el-table-column
align="center"
label="扩容日期"
prop="created_time">
<template slot-scope="scope">
{{ format_time(scope.row.created_time) }}
</template>
</el-table-column>
<el-table-column
align="center"
label="失效日期"
prop="expires_time">
<template slot-scope="scope">
{{ format_time(scope.row.expires_time) }}
</template>
</el-table-column>
<el-table-column
align="center"
label="状态"
prop="expires_time"
width="160">
<template slot-scope="scope">
<el-popover :content="expire_content(scope.row.is_expired)" placement="top-start" trigger="hover">
<div slot="reference">
<el-tag v-if="scope.row.is_expired > 0">生效中</el-tag>
<el-tag v-else type="warning">已失效</el-tag>
</div>
</el-popover>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="存储设置" name="setting" style="text-align: center">
<div v-if="migrate_progress && migrate_progress.s_time" style="text-align: left">
<el-card>
@ -770,7 +870,7 @@
</template>
<script>
import {cleanStorageData, configStorageData, getStorageinfo, shareStorageData} from "@/restful";
import {cleanStorageData, configStorageData, exchangeStorageData, getStorageinfo, shareStorageData} from "@/restful";
import {deepCopy, diskSize, getFormatDate, getUserInfoFun} from "@/utils";
import {format_time} from "@/utils/base/utils";
@ -790,9 +890,11 @@ export default {
target_number: '',
fstorage_lists: [],
share_bill_lists: [],
storage_exchange_lists: [],
Sdisabled: true,
canshare: true,
use_storage_id: 0,
exchange_number: 10,
org_storage_id: 0,
title: '',
uidsearch: '',
@ -801,6 +903,8 @@ export default {
dialogstorageVisible: false,
editstorageinfo: {},
migrate_progress: {},
exchange_prices: {},
exchange_prices_key: "",
selectlabel: "",
storageinfo: {'used_number': 0, 'max_storage_capacity': 1},
storage_list: [],
@ -808,6 +912,7 @@ export default {
disabled: true,
isaddflag: false,
shareVisible: false,
exchangeVisible: false,
showshareSync: false,
activeName: 'change',
storage_info_lists: [],
@ -819,8 +924,38 @@ export default {
format_time,
diskSize,
getFormatDate,
cancelStorage() {
sureExchangeFun(exchange_number, exchange_prices_key) {
exchangeStorageData(res => {
if (res.code === 1000) {
this.$message.success("扩容成功")
this.exchangeVisible = false
} else {
this.$message.error("操作失败了," + res.msg)
}
}, {"methods": 'POST', "data": {exchange_number, exchange_prices_key}})
},
exchangeFun() {
exchangeStorageData(res => {
if (res.code === 1000) {
this.exchangeVisible = true
this.exchange_prices = res.data
let pay_times = this.exchange_prices.pay_times
if (pay_times && pay_times.length > 0) {
this.exchange_prices_key = pay_times[0].name
}
} else {
this.$message.error("获取数据失败了," + res.msg)
}
}, {"methods": 'PUT', "data": new Date()})
},
expire_content(is_expired) {
if (is_expired > 0) {
return `生效中,还有 ${is_expired} 天到期`
}
return `${-is_expired} 天前已失效`
},
cancelStorage() {
this.$confirm("确定要解除迁移锁么,在未完成迁移状态下解除迁移锁之后,可能会导致数据异常?", '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
@ -1241,7 +1376,9 @@ export default {
}, {"methods": 'GET', "data": {'act': 'storage_type'}});
} else if (tabname === "share") {
data.operatestatus = this.operatestatus
this.loading = true
shareStorageData(res => {
this.loading = false
if (res.code === 1000) {
this.share_bill_lists = res.data
this.pagination.total = res.count
@ -1260,13 +1397,25 @@ export default {
}
}, {"methods": 'GET', "data": data})
} else if (tabname === 'exchange') {
this.loading = true
exchangeStorageData(res => {
if (res.code === 1000) {
this.storage_exchange_lists = res.data
this.pagination.total = res.count
} else {
this.$message.error("获取数据失败了," + res.msg)
}
this.loading = false
}, {"methods": 'GET', "data": data})
}
},
}, mounted() {
getUserInfoFun(this);
if (this.$route.params.act) {
let activeName = this.$route.params.act;
let activeName_list = ["change", "edit", "add", "share", "setting"];
let activeName_list = ["change", "edit", "add", "share", "exchange", "setting"];
for (let index in activeName_list) {
if (activeName_list[index] === activeName) {
this.activeName = activeName;
@ -1294,6 +1443,14 @@ export default {
p = 100
}
return p
},
exchangetimes() {
let pay_key = this.exchange_prices_key
if (pay_key) {
let m = pay_key.split('_')[1]
return this.exchange_number * parseInt(m) * this.exchange_prices.storage_every_month_price / this.exchange_prices.download_times
}
return 0
}
}
}

@ -414,6 +414,18 @@ export function configStorageData(callBack, params) {
);
}
/**用户存储空间兑换操作 */
export function exchangeStorageData(callBack, params) {
getData(
params.methods,
USERSEVER + '/storage/exchange',
params.data,
data => {
callBack(data);
}
);
}
/**用户个人信息 */
export function userinfos(callBack, params) {
getData(

@ -254,6 +254,7 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=128, unique=True, verbose_name='下载包唯一名称')),
('title', models.CharField(max_length=128, verbose_name='下载包名称')),
('description', models.CharField(max_length=128, verbose_name='下载包描述')),
('price_type', models.SmallIntegerField(choices=[(1, '下载次数'), (2, '存储容量')], default=1)),
('price', models.BigIntegerField(verbose_name='下载包价格,单位分')),
('package_size', models.BigIntegerField(verbose_name='下载包次数')),
('download_count_gift', models.IntegerField(default=0, verbose_name='赠送下载次数')),

@ -10,6 +10,7 @@ price_info_list = [
"title": "1k_times",
"description": "1k_times",
"price": 2500,
"price_type": 1,
"package_size": 1000,
"download_count_gift": 100,
"is_enable": True,
@ -19,6 +20,7 @@ price_info_list = [
"title": "10k_times",
"description": "10k_times",
"price": 22000,
"price_type": 1,
"package_size": 10000,
"download_count_gift": 800,
"is_enable": True,
@ -28,10 +30,58 @@ price_info_list = [
"title": "100k_times",
"description": "100k_times",
"price": 200000,
"price_type": 1,
"package_size": 100000,
"download_count_gift": 8000,
"is_enable": True,
},
{
"name": "storage_3_month",
"title": "三个月",
"description": "三个月",
"price": 3,
"price_type": 2,
"package_size": 3,
"download_count_gift": 0,
"is_enable": True,
}, {
"name": "storage_6_month",
"title": "半年",
"description": "半年",
"price": 6,
"price_type": 2,
"package_size": 6,
"download_count_gift": 0,
"is_enable": True,
}, {
"name": "storage_12_month",
"title": "一年",
"description": "一年",
"price": 12,
"price_type": 2,
"package_size": 12,
"download_count_gift": 0,
"is_enable": True,
}, {
"name": "storage_24_month",
"title": "两年",
"description": "两年",
"price": 24,
"price_type": 2,
"package_size": 24,
"download_count_gift": 0,
"is_enable": True,
}, {
"name": "storage_36_month",
"title": "三年",
"description": "三年",
"price": 36,
"price_type": 2,
"package_size": 36,
"download_count_gift": 0,
"is_enable": True,
}
]
WEB_DOMAIN = DOMAINCONF.WEB_DOMAIN.split('//')[1]
domain_cname_info_list = [

@ -16,11 +16,6 @@ class Migration(migrations.Migration):
name='max_storage_capacity',
field=models.BigIntegerField(default=0, verbose_name='存储最大使用容量,单位btype'),
),
migrations.AddField(
model_name='userinfo',
name='storage_capacity',
field=models.BigIntegerField(default=0, verbose_name='存储容量,单位byte'),
),
migrations.AlterField(
model_name='userinfo',
name='download_times',
@ -62,4 +57,22 @@ class Migration(migrations.Migration):
'verbose_name_plural': '私有存储共享',
},
),
migrations.CreateModel(
name='StorageExchange',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('download_times', models.BigIntegerField(default=0, verbose_name='消耗下载次数')),
('storage_size', models.BigIntegerField(default=0, verbose_name='兑换空间大小')),
('expires_time', models.DateTimeField(verbose_name='失效时间')),
('description', models.CharField(blank=True, default='', max_length=128, verbose_name='操作描述')),
('remote_addr', models.GenericIPAddressField(verbose_name='远程IP地址')),
('created_time', models.DateTimeField(auto_now_add=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': '存储空间兑换信息',
},
),
]

@ -40,7 +40,6 @@ class UserInfo(AbstractUser):
memo = models.TextField('备注', blank=True, null=True, default=None, )
date_joined = models.DateTimeField(auto_now_add=True, verbose_name="注册时间")
download_times = models.PositiveBigIntegerField(default=0, verbose_name="可用下载点次数,需要用户充值")
storage_capacity = models.BigIntegerField(default=0, verbose_name="存储容量,单位byte")
all_download_times = models.BigIntegerField(default=0, verbose_name="总共下载次数")
default_domain_name = models.ForeignKey(to="DomainCnameInfo", verbose_name="默认下载页域名", on_delete=models.CASCADE)
history_release_limit = models.IntegerField(default=10, verbose_name="app 历史记录版本", blank=True, null=True)
@ -318,6 +317,8 @@ class Price(models.Model):
name = models.CharField(max_length=128, unique=True, verbose_name="下载包唯一名称")
title = models.CharField(max_length=128, verbose_name="下载包名称")
description = models.CharField(max_length=128, verbose_name="下载包描述")
price_type_choices = ((1, '下载次数'), (2, '存储容量'))
price_type = models.SmallIntegerField(choices=price_type_choices, default=1)
price = models.BigIntegerField(null=False, verbose_name="下载包价格,单位分")
package_size = models.BigIntegerField(null=False, verbose_name="下载包次数")
download_count_gift = models.IntegerField(default=0, null=False, verbose_name="赠送下载次数")
@ -330,12 +331,13 @@ class Price(models.Model):
unique_together = ('price', 'package_size')
def save(self, *args, **kwargs):
if Price.objects.filter(is_enable=True).count() > 3: # 最多3个启用的价格表
if Price.objects.filter(is_enable=True, price_type=1).count() > 3: # 最多3个启用的价格表
raise
super(Price, self).save(*args, **kwargs)
def __str__(self):
return "%s-%s-%s-%s" % (self.name, self.price, self.package_size, self.download_count_gift)
return "%s-%s-%s-%s-%s" % (
self.name, self.price, self.package_size, self.download_count_gift, self.get_price_type_display())
class UserCertificationInfo(models.Model):
@ -604,3 +606,20 @@ class StorageShareInfo(models.Model):
def __str__(self):
return f"{self.user_id}-{self.to_user_id}{self.description}"
class StorageExchange(models.Model):
user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE)
download_times = models.BigIntegerField(verbose_name="消耗下载次数", default=0)
storage_size = models.BigIntegerField(verbose_name="兑换空间大小", default=0)
expires_time = models.DateTimeField(verbose_name="失效时间")
description = models.CharField(verbose_name="操作描述", max_length=128, default='', blank=True)
remote_addr = models.GenericIPAddressField(verbose_name="远程IP地址")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间")
class Meta:
verbose_name = '存储空间兑换信息'
verbose_name_plural = "存储空间兑换信息"
def __str__(self):
return f"{self.user_id}{self.description}"

@ -29,7 +29,7 @@ from api.views.notify import NotifyReceiverView, NotifyConfigView, NotifyInfoVie
from api.views.order import PriceView, OrderView, PaySuccess, OrderSyncView
from api.views.personalconfig import PersonalConfigView
from api.views.report import ReportView
from api.views.storage import StorageView, CleanStorageView, ShareStorageView, StorageConfigView
from api.views.storage import StorageView, CleanStorageView, ShareStorageView, StorageConfigView, StorageExchangeView
from api.views.thirdlogin import ValidWxChatToken, ThirdWxAccount
from api.views.uploads import AppAnalyseView, UploadView
@ -50,6 +50,7 @@ urlpatterns = [
re_path("^storage/clean$", CleanStorageView.as_view()),
re_path("^storage/share$", ShareStorageView.as_view()),
re_path("^storage/config$", StorageConfigView.as_view()),
re_path("^storage/exchange$", StorageExchangeView.as_view()),
re_path(r"^apps/(?P<app_id>\w+)", AppInfoView.as_view()),
re_path(r"^download_password/(?P<app_id>\w+)", AppDownloadTokenView.as_view()),
re_path(r"^appinfos/(?P<app_id>\w+)/(?P<act>\w+)", AppReleaseInfoView.as_view()),

@ -3,8 +3,7 @@
# project: 4月
# author: NinEveN
# date: 2021/4/16
import datetime
import logging
import random
from urllib.parse import urljoin
@ -13,21 +12,24 @@ from django.db.models import Count
from rest_framework.pagination import PageNumberPagination
from api.models import AppReleaseInfo, UserDomainInfo, DomainCnameInfo, UserAdDisplayInfo, RemoteClientInfo, \
AppBundleIdBlackList, NotifyReceiver, WeChatInfo, AppDownloadToken, Apps, StorageShareInfo, UserInfo, AppStorage
AppBundleIdBlackList, NotifyReceiver, WeChatInfo, AppDownloadToken, Apps, StorageShareInfo, UserInfo, AppStorage, \
StorageExchange
from common.base.baseutils import get_server_domain_from_request, get_user_default_domain_name, get_real_ip_address, \
get_origin_domain_name
from common.base.magic import MagicCacheData
from common.core.sysconfig import Config
from common.core.sysconfig import Config, UserConfig
logger = logging.getLogger(__name__)
def get_app_d_count_by_app_id(app_id, is_oss=False):
def get_app_d_count_by_app_id(app_id, user_obj):
d_count = 1
is_oss = user_obj.storage
binary_size = AppReleaseInfo.objects.filter(is_master=True, app_id__app_id=app_id).values('binary_size').first()
if binary_size and binary_size.get('binary_size', 0) > 0:
d_count += binary_size.get('binary_size') // 1024 // 1024 // 100
return d_count * (Config.APP_USE_BASE_DOWNLOAD_TIMES if not is_oss else Config.PRIVATE_OSS_DOWNLOAD_TIMES)
d_count += binary_size.get('binary_size') // Config.APP_FILE_CALCULATION_UNIT
return d_count * (UserConfig(user_obj).APP_USE_BASE_DOWNLOAD_TIMES if not is_oss else UserConfig(
user_obj).PRIVATE_OSS_DOWNLOAD_TIMES)
def get_user_domain_name(obj, domain_type=1):
@ -277,11 +279,18 @@ def get_user_storage_used(user_obj):
return binary_size_sum
def get_user_exchange_storage_capacity(user_obj):
obj = StorageExchange.objects.filter(user_id=user_obj, expires_time__gte=datetime.datetime.now()).order_by(
'storage_size').last()
if obj:
return obj.storage_size
return 0
def get_user_storage_capacity(user_obj):
storage_obj = user_obj.storage
if not storage_obj:
user_storage_capacity = user_obj.storage_capacity
storage_capacity = user_storage_capacity if user_storage_capacity else Config.STORAGE_FREE_CAPACITY
storage_capacity = get_user_exchange_storage_capacity(user_obj) + Config.STORAGE_FREE_CAPACITY
else:
if storage_obj.user_id == user_obj:
max_storage_capacity = storage_obj.max_storage_capacity

@ -1,3 +1,4 @@
import datetime
import logging
import os
@ -9,7 +10,7 @@ from api.utils.apputils import bytes2human
from api.utils.modelutils import get_user_domain_name, get_app_domain_name, get_app_download_uri, get_user_storage_used
from common.base.baseutils import get_choices_dict, WeixinLoginUid
from common.cache.storage import AdPicShowCache
from common.core.sysconfig import Config
from common.core.sysconfig import Config, UserConfig
from common.utils.caches import get_user_free_download_times, get_user_cert_auth_status
from common.utils.storage import Storage
from common.utils.token import make_token
@ -61,9 +62,18 @@ def get_screenshots_from_self(self, obj, force_new=False):
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = ["username", "uid", "mobile", "job", "email", "domain_name", "role", "first_name",
fields = ["username", "uid", "mobile", "job", "email", "domain_name", "role", "first_name", "times_info",
'head_img', 'storage_active', 'supersign_active', 'free_download_times', 'download_times',
'certification', 'qrcode_domain_name', 'storage_used', 'storage_capacity', 'storage_used_capacity']
'certification', 'qrcode_domain_name', 'storage_used', 'storage_used_capacity']
times_info = serializers.SerializerMethodField()
def get_times_info(self, obj):
return {
'every_size': UserConfig(obj).APP_FILE_CALCULATION_UNIT,
'private_times': UserConfig(obj).PRIVATE_OSS_DOWNLOAD_TIMES,
'base_times': UserConfig(obj).APP_USE_BASE_DOWNLOAD_TIMES,
}
storage_used_capacity = serializers.SerializerMethodField()
@ -436,7 +446,7 @@ class StorageSerializer(serializers.ModelSerializer):
class PriceSerializer(serializers.ModelSerializer):
class Meta:
model = models.Price
exclude = ["id"]
exclude = ["id", "price_type", "is_enable"]
class OrdersSerializer(serializers.ModelSerializer):
@ -590,3 +600,14 @@ class StorageShareSerializer(serializers.ModelSerializer):
def get_cancel(self, obj):
return self.context.get('user_obj').pk == obj.user_id.pk
class StorageExchangeSerializer(serializers.ModelSerializer):
class Meta:
model = models.StorageExchange
exclude = ["id", "remote_addr", "user_id"]
is_expired = serializers.SerializerMethodField()
def get_is_expired(self, obj):
return (obj.expires_time - datetime.datetime.now()).days

@ -198,7 +198,7 @@ def migrating_storage_data(user_obj, new_storage_obj, clean_old_data):
# 消费下载次数
cache_default_data['migrate_num'] += 1
task_cache.set_storage_cache(cache_default_data)
amount = get_app_d_count_by_app_id(app_release_obj.app_id.app_id, False)
amount = get_app_d_count_by_app_id(app_release_obj.app_id.app_id, user_obj)
consume_user_download_times(user_obj.pk, app_release_obj.app_id, amount, auth_status)
task_cache.del_storage_cache()
return True

@ -61,7 +61,7 @@ class OrderView(APIView):
logger.info(f"{request.user} 下单成功 {res.dict}")
return Response(res.dict)
price_obj = Price.objects.filter(name=price_id).first()
price_obj = Price.objects.filter(name=price_id, price_type=1).first()
if price_obj:
try:
order_number = get_order_num()
@ -154,7 +154,8 @@ class PriceView(APIView):
def get(self, request):
res = BaseResponse()
price_obj_lists = Price.objects.filter(is_enable=True).all().order_by("updated_time").order_by("price")
price_obj_lists = Price.objects.filter(is_enable=True, price_type=1).all().order_by("updated_time").order_by(
"price")
res.data = PriceSerializer(price_obj_lists, many=True).data
res.pay_choices = get_enable_pay_choices()
return Response(res.dict)

@ -3,7 +3,6 @@
# project: 3月
# author: liuyu
# date: 2020/3/4
import logging
from django.db.models import Q
@ -11,19 +10,19 @@ from rest_framework.response import Response
from rest_framework.views import APIView
from api.base_views import app_delete
from api.models import AppStorage, Apps, StorageShareInfo, UserInfo
from api.models import AppStorage, Apps, StorageShareInfo, UserInfo, StorageExchange, Price
from api.tasks import migrate_storage_job
from api.utils.apputils import clean_history_apps
from api.utils.modelutils import PageNumber, get_user_storage_capacity, get_user_storage_used
from api.utils.response import BaseResponse
from api.utils.serializer import StorageSerializer, StorageShareSerializer
from api.utils.serializer import StorageSerializer, StorageShareSerializer, StorageExchangeSerializer, PriceSerializer
from api.utils.utils import upload_oss_default_head_img
from common.base.baseutils import get_choices_dict, get_choices_name_from_key, get_real_ip_address
from common.cache.state import MigrateStorageState
from common.cache.storage import TaskProgressCache
from common.core.auth import ExpiringTokenAuthentication, StoragePermission
from common.core.sysconfig import Config
from common.utils.caches import del_cache_storage
from common.utils.caches import del_cache_storage, add_user_storage_exchange
logger = logging.getLogger(__name__)
@ -93,10 +92,9 @@ class StorageView(APIView):
return Response(res.dict)
if pk:
if pk == '-1':
storage_capacity = request.user.storage_capacity
default_storage = {
'id': -1,
'max_storage_capacity': storage_capacity if storage_capacity != 0 else Config.STORAGE_FREE_CAPACITY,
'max_storage_capacity': get_user_storage_capacity(request.user),
'used_number': get_user_storage_used(request.user)
}
res.data = [default_storage]
@ -305,10 +303,10 @@ class ShareStorageView(APIView):
except Exception as e:
logger.error(f'status {status} check failed Exception:{e}')
app_page_serializer = page_obj.paginate_queryset(queryset=share_list.order_by("-created_time"),
request=request,
view=self)
app_serializer = StorageShareSerializer(app_page_serializer, many=True, context={'user_obj': request.user})
page_serializer = page_obj.paginate_queryset(queryset=share_list.order_by("-created_time"),
request=request,
view=self)
app_serializer = StorageShareSerializer(page_serializer, many=True, context={'user_obj': request.user})
res.data = app_serializer.data
res.count = share_list.count()
res.status_choices = get_choices_dict(StorageShareInfo.status_choices)
@ -461,3 +459,49 @@ class StorageConfigView(APIView):
if migrate_obj.get_state() == request.data.get('storage_status'):
migrate_obj.del_state()
return Response(res.dict)
class StorageExchangeView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
permission_classes = [StoragePermission, ]
def get(self, request):
res = BaseResponse()
storage_exchange_queryset = StorageExchange.objects.filter(user_id=request.user)
page_obj = PageNumber()
page_serializer = page_obj.paginate_queryset(queryset=storage_exchange_queryset.order_by("-created_time"),
request=request,
view=self)
app_serializer = StorageExchangeSerializer(page_serializer, many=True)
res.data = app_serializer.data
res.count = storage_exchange_queryset.count()
return Response(res.dict)
def post(self, request):
res = BaseResponse()
exchange_number = request.data.get('exchange_number')
exchange_prices_key = request.data.get('exchange_prices_key')
if exchange_number and exchange_prices_key:
price_obj = Price.objects.filter(is_enable=True, price_type=2, name=exchange_prices_key).first()
if price_obj:
exchange_month = price_obj.price
if not add_user_storage_exchange(request.user, exchange_number, exchange_month,
get_real_ip_address(request)):
res.code = 1002
res.msg = '扩容失败,账号可用下载次数不足'
else:
res.code = 1001
res.msg = '价格异常'
return Response(res.dict)
def put(self, request):
res = BaseResponse()
price_obj_lists = Price.objects.filter(is_enable=True, price_type=2).all().order_by("updated_time").order_by(
"price")
res.data = {
'pay_times': PriceSerializer(price_obj_lists, many=True).data,
'storage_every_month_price': Config.OSS_EXCHANGE_DOWNLOAD_TIMES,
'download_times': Config.APP_USE_BASE_DOWNLOAD_TIMES,
}
return Response(res.dict)

@ -319,6 +319,14 @@ class UserDownloadTimesCache(ConfigCacheBase):
def PRIVATE_OSS_DOWNLOAD_TIMES(self):
return super().get_value('PRIVATE_OSS_DOWNLOAD_TIMES', DOWNLOADTIMESCONF.PRIVATE_OSS_DOWNLOAD_TIMES)
@property
def APP_FILE_CALCULATION_UNIT(self):
return super().get_value('APP_FILE_CALCULATION_UNIT', DOWNLOADTIMESCONF.APP_FILE_CALCULATION_UNIT)
@property
def OSS_EXCHANGE_DOWNLOAD_TIMES(self):
return super().get_value('OSS_EXCHANGE_DOWNLOAD_TIMES', DOWNLOADTIMESCONF.OSS_EXCHANGE_DOWNLOAD_TIMES)
class OssStorageConfCache(ConfigCacheBase):
def __init__(self, *args, **kwargs):

@ -3,7 +3,7 @@
# project: 4月
# author: liuyu
# date: 2020/4/7
import datetime
import logging
import time
@ -11,7 +11,7 @@ from django.core.cache import cache
from django.db.models import F
from django.utils import timezone
from api.models import Apps, UserInfo, UserCertificationInfo, Order
from api.models import Apps, UserInfo, UserCertificationInfo, Order, StorageExchange
from api.utils.modelutils import get_app_d_count_by_app_id, get_app_domain_name, get_user_domain_name
from common.base.baseutils import get_order_num
from common.cache.invalid import invalid_app_cache, invalid_short_cache, invalid_app_download_times_cache, \
@ -228,14 +228,11 @@ def get_user_cert_auth_status(user_id):
def check_user_has_all_download_times(app_obj, user_obj):
if user_obj:
user_id = user_obj.pk
storage = user_obj.storage
else:
user_id = app_obj.user_id_id
storage = app_obj.user_id.storage
if not user_obj:
user_obj = app_obj.user_id
user_id = user_obj.pk
auth_status = get_user_cert_auth_status(user_id)
d_count = get_app_d_count_by_app_id(app_obj.app_id, storage)
d_count = get_app_d_count_by_app_id(app_obj.app_id, user_obj)
a = get_user_free_download_times(user_id, auth_status=auth_status)
b = d_count
c = check_user_can_download(user_id)
@ -244,10 +241,10 @@ def check_user_has_all_download_times(app_obj, user_obj):
def consume_user_download_times_by_app_obj(app_obj):
user_id = app_obj.user_id_id
auth_status = get_user_cert_auth_status(user_id)
amount = get_app_d_count_by_app_id(app_obj.app_id, False)
if consume_user_download_times(user_id, app_obj.app_id, int(amount * (Config.SIGN_EXTRA_MULTIPLE - 1)),
user_obj = app_obj.user_id
auth_status = get_user_cert_auth_status(user_obj.pk)
amount = get_app_d_count_by_app_id(app_obj.app_id, user_obj)
if consume_user_download_times(user_obj.pk, app_obj.app_id, int(amount * (Config.SIGN_EXTRA_MULTIPLE - 1)),
auth_status):
return False
return True
@ -398,3 +395,24 @@ def get_and_clean_udid_cache_queue(prefix_key):
data = []
cache_obj.del_storage_cache()
return data
def add_user_storage_exchange(user_obj, exchange_number, exchange_month, remote_addr):
with cache.lock("%s_%s" % ('user_storage_order_', user_obj.uid), timeout=10, blocking_timeout=6):
try:
download_times = exchange_number * exchange_month * Config.OSS_EXCHANGE_DOWNLOAD_TIMES
expires_time = datetime.datetime.now() + datetime.timedelta(days=30 * exchange_month)
description = f'扩容成功,消费下载点数{download_times} 兑换了 {exchange_number} GB存储空间'
obj = StorageExchange.objects.create(user_id=user_obj, download_times=download_times,
storage_size=exchange_number * 1024 * 1024 * 1024,
expires_time=expires_time, remote_addr=remote_addr,
description=description)
if add_user_download_times(user_obj.pk, -download_times):
logger.info(f"{user_obj} 存储扩容成功 msg:{description}")
return True
else:
obj.delete()
except Exception as e:
logger.error(f"{user_obj} download_times less then 0. Exception:{e}")
return False

@ -71,7 +71,8 @@ def get_app_instance_by_cache(app_id, limit):
'issupersign', 'user_id__storage',
'user_id__certification__status').first()
if app_obj_cache:
app_obj_cache['d_count'] = get_app_d_count_by_app_id(app_id, app_obj_cache.get('user_id__storage', False))
user_obj = UserInfo.objects.filter(pk=app_obj_cache.get('user_id')).first()
app_obj_cache['d_count'] = get_app_d_count_by_app_id(app_id, user_obj)
app_instance_cache.set_storage_cache(app_obj_cache, limit)
if not app_obj_cache:
return False, '应用不存在'

@ -392,7 +392,12 @@ bIX1aWjPxirQX9mzaL3oEQI=
class DOWNLOADTIMESCONF(object):
SIGN_EXTRA_MULTIPLE = 2 # 超级签名消耗额外倍数,超级签名需要占用的服务大量资源
# 具体计算方式查看该函数 get_app_d_count_by_app_id
APP_USE_BASE_DOWNLOAD_TIMES = 100 # 单个应用下载消费点数
APP_FILE_CALCULATION_UNIT = 1024 * 1024 * 100 # 应用计算次数,表示应用大小计算点数
OSS_EXCHANGE_DOWNLOAD_TIMES = "{% widthratio 5 1 {{APP_USE_BASE_DOWNLOAD_TIMES}} %}" # 默认 1G 一个月 500下载点数
PRIVATE_OSS_DOWNLOAD_TIMES = 4 # 私有存储单个应用下载消费点数
USER_FREE_DOWNLOAD_TIMES = "{% widthratio 5 1 {{APP_USE_BASE_DOWNLOAD_TIMES}} %}" # 用户每日免费下载点数

Loading…
Cancel
Save