增加存储共享功能,增加应用历史版本设置,修复设备共享查询问题,优化存储切换逻辑

dependabot/npm_and_yarn/fir_admin/eventsource-1.1.1
nineven 3 years ago
parent 0c6b61327a
commit a5b73c3695
  1. 5
      fir_client/src/components/apps/FirAppInfosbaseinfo.vue
  2. 6
      fir_client/src/components/apps/FirApps.vue
  3. 14
      fir_client/src/components/user/FirSuperSignBase.vue
  4. 780
      fir_client/src/components/user/FirUserStorage.vue
  5. 24
      fir_client/src/restful/index.js
  6. 2
      fir_client/src/utils/index.js
  7. 13
      fir_ser/api/base_views.py
  8. 60
      fir_ser/api/migrations/0006_auto_20220513_0843.py
  9. 32
      fir_ser/api/models.py
  10. 4
      fir_ser/api/urls.py
  11. 10
      fir_ser/api/utils/apputils.py
  12. 48
      fir_ser/api/utils/modelutils.py
  13. 75
      fir_ser/api/utils/serializer.py
  14. 17
      fir_ser/api/utils/utils.py
  15. 10
      fir_ser/api/views/apps.py
  16. 270
      fir_ser/api/views/storage.py
  17. 45
      fir_ser/api/views/uploads.py
  18. 44
      fir_ser/common/base/magic.py
  19. 25
      fir_ser/common/core/sysconfig.py
  20. 9
      fir_ser/common/libs/storage/aliyunApi.py
  21. 10
      fir_ser/common/libs/storage/localApi.py
  22. 10
      fir_ser/common/libs/storage/qiniuApi.py
  23. 12
      fir_ser/common/notify/notify.py
  24. 2
      fir_ser/common/utils/caches.py
  25. 7
      fir_ser/common/utils/storage.py
  26. 10
      fir_ser/config.py
  27. 3
      fir_ser/xsign/utils/supersignutils.py
  28. 26
      fir_ser/xsign/views/supersign.py

@ -12,9 +12,10 @@
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="使用空间" style="width: 56%">
<el-input v-model="currentapp.storage_used" :disabled="true" :value="currentapp.storage_used"/>
</el-form-item>
<el-form-item label="应用名称" style="width: 56%">
<el-input v-model="currentapp.name"/>
</el-form-item>

@ -746,7 +746,11 @@ export default {
loading.close();
}, {
'methods': 'POST',
'data': {"bundleid": analyseappinfo.bundleid, "type": analyseappinfo.type}
'data': {
"bundleid": analyseappinfo.bundleid,
"type": analyseappinfo.type,
"filesize": analyseappinfo.filesize
}
});
} else {

@ -1310,15 +1310,18 @@
v-model="uidsearch"
clearable
placeholder="输入用户UID"
style="width: 30%;margin-right: 30px;margin-bottom: 10px"/>
style="width: 30%;margin-right: 20px;margin-bottom: 10px"/>
<el-select v-model="operatestatus" clearable placeholder="操作状态"
style="width: 120px;margin-right: 20px" @change="handleCurrentChange(1)">
<el-option v-for="item in operate_status_choices" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
<el-button icon="el-icon-search" type="primary" @click="handleCurrentChange(1)">
搜索
</el-button>
<el-button icon="el-icon-s-unfold" plain @click="transferVisible=true">
设备共享
</el-button>
<div style="width: 40%;margin-right: 30px;float:right">
<div style="width: 35%;margin-right: 10px;float:right">
<el-link :underline="false">共享签名池设备数量{{ balance_info.all_balance }} 已经使用 {{
balance_info.used_balance
}}
@ -2071,9 +2074,10 @@ export default {
this.cantransfer = false
} else {
this.cantransfer = true
this.transferInfo = {}
this.$message.error("查询失败 " + data.msg)
}
}, {'methods': 'PUT', data: {uid: uid}})
}, {'methods': 'PUT', data: {uid: uid, 'act': 'check'}})
},
get_developer_uid(uid) {
if (uid && uid.indexOf(':') > -1) {
@ -2493,6 +2497,7 @@ export default {
this.iosdevicebillFun({"methods": "GET", "data": data})
} else if (tabname === "transferbill") {
data.uidsearch = this.uidsearch.replace(/^\s+|\s+$/g, "");
data.operatestatus = this.operatestatus;
this.deviceTransferFun({"methods": "GET", "data": data})
} else if (tabname === "devicesrank") {
if (this.timerangesearch && this.timerangesearch.length === 2) {
@ -2636,6 +2641,7 @@ export default {
DeviceTransferBillInfo(data => {
if (data.code === 1000) {
this.transfer_bill_lists = data.data;
this.operate_status_choices = data.status_choices
this.pagination.total = data.count;
if (data.balance_info) {
this.balance_info = data.balance_info;

File diff suppressed because it is too large Load Diff

@ -390,6 +390,30 @@ export function cleanStorageData(callBack, params) {
);
}
/**用户存储清理操作 */
export function shareStorageData(callBack, params) {
getData(
params.methods,
USERSEVER + '/storage/share',
params.data,
data => {
callBack(data);
}
);
}
/**用户存储清理操作 */
export function configStorageData(callBack, params) {
getData(
params.methods,
USERSEVER + '/storage/config',
params.data,
data => {
callBack(data);
}
);
}
/**用户个人信息 */
export function userinfos(callBack, params) {
getData(

@ -626,7 +626,7 @@ export function diskSize(num) {
let k = 1024; //设定基础容量大小
let sizeStr = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; //容量单位
let i = 0; //单位下标和次幂
for (let l = 0; l < 8; l++) { //因为只有8个单位所以循环八次
for (let l = 0; l < 9; l++) { //因为只有8个单位所以循环八次
if (num / Math.pow(k, l) < 1) { //判断传入数值 除以 基础大小的次幂 是否小于1,这里小于1 就代表已经当前下标的单位已经不合适了所以跳出循环
break; //小于1跳出循环
}

@ -10,7 +10,8 @@ from api.models import AppReleaseInfo, UserInfo, AppScreenShot, AppStorage
from api.utils.response import BaseResponse
from api.utils.signalutils import run_delete_app_signal
from api.utils.utils import delete_local_files, delete_app_screenshots_files, change_storage_and_change_head_img, \
migrating_storage_data, clean_storage_data, check_storage_is_new_storage
migrating_storage_data, clean_storage_data, check_storage_is_new_storage, migrating_storage_file_data
from common.base.magic import MagicCacheData
from common.cache.state import MigrateStorageState
from common.utils.caches import del_cache_response_by_short, del_cache_by_delete_app, \
del_cache_storage
@ -50,6 +51,7 @@ def app_delete(app_obj):
has_combo.has_combo = None
has_combo.save(update_fields=['has_combo'])
MagicCacheData.invalid_cache(app_obj.app_id)
app_obj.delete()
return res
@ -101,12 +103,19 @@ def app_release_delete(app_obj, release_id, storage):
return res
def check_storage_ok(user_obj, new_storage_obj):
if migrating_storage_file_data(user_obj, 'head_img.jpeg', new_storage_obj, False):
return True
def storage_change(use_storage_id, user_obj, force):
if use_storage_id:
if user_obj.storage and use_storage_id == user_obj.storage.id:
return True
try:
if use_storage_id == -1:
if not check_storage_ok(user_obj, None):
return False
change_storage_and_change_head_img(user_obj, None)
if migrating_storage_data(user_obj, None, False):
if check_storage_is_new_storage(user_obj, None):
@ -114,6 +123,8 @@ def storage_change(use_storage_id, user_obj, force):
UserInfo.objects.filter(pk=user_obj.pk).update(storage=None)
else:
new_storage_obj = AppStorage.objects.filter(pk=use_storage_id).first()
if not check_storage_ok(user_obj, new_storage_obj):
return False
change_storage_and_change_head_img(user_obj, new_storage_obj)
if migrating_storage_data(user_obj, new_storage_obj, False):
if check_storage_is_new_storage(user_obj, new_storage_obj):

@ -0,0 +1,60 @@
# Generated by Django 3.2.3 on 2022-05-13 08:43
import django.db.models.fields
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0005_auto_20220412_1744'),
]
operations = [
migrations.AddField(
model_name='appstorage',
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='storage',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL,
related_name='app_storage', to='api.appstorage', verbose_name='存储'),
),
migrations.AlterUniqueTogether(
name='appstorage',
unique_together={('user_id', 'bucket_name', 'endpoint'), ('user_id', 'name')},
),
migrations.CreateModel(
name='StorageShareInfo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status',
models.SmallIntegerField(choices=[(1, '共享成功'), (2, '取消共享')], default=0, help_text='1 共享成功 2 取消共享',
verbose_name='状态')),
('number', models.BigIntegerField(default=0, verbose_name='容量大小,单位byte')),
('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='添加时间')),
('updated_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('storage_id', models.ForeignKey(on_delete=django.db.models.fields.CharField, to='api.appstorage',
verbose_name='存储ID')),
('to_user_id', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE,
related_name='storage_to_user_id', to=settings.AUTH_USER_MODEL,
verbose_name='用户ID')),
('user_id',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='storage_org_user_id',
to=settings.AUTH_USER_MODEL, verbose_name='用户ID')),
],
options={
'verbose_name': '私有存储共享',
'verbose_name_plural': '私有存储共享',
},
),
]

@ -40,10 +40,11 @@ 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.PositiveIntegerField(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)
storage = models.OneToOneField(to='AppStorage', related_name='app_storage',
storage = models.ForeignKey(to='AppStorage', related_name='app_storage',
on_delete=models.SET_NULL, verbose_name="存储", null=True, blank=True)
api_token = models.CharField(max_length=256, verbose_name='api访问密钥', default='')
notify_available_downloads = models.IntegerField(default=0, verbose_name="下载余额不足通知", blank=True, null=True)
@ -261,6 +262,8 @@ class AppStorage(models.Model):
verbose_name="阿里云下载授权方式")
cnd_auth_key = models.CharField(max_length=128, blank=True, null=True, verbose_name="阿里云cnd_auth_key")
max_storage_capacity = models.BigIntegerField(verbose_name="存储最大使用容量,单位btype", default=0)
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
description = models.TextField('备注', blank=True, null=True, default='')
@ -268,6 +271,7 @@ class AppStorage(models.Model):
class Meta:
verbose_name = '存储配置'
verbose_name_plural = "存储配置"
unique_together = (('user_id', 'name'), ('user_id', 'bucket_name', 'endpoint'))
def save(self, *args, **kwargs):
if self.storage_type in (1, 2):
@ -574,3 +578,29 @@ class UserPersonalConfig(BaseConfig):
def __str__(self):
return "%s-%s" % (self.key, self.description)
class StorageShareInfo(models.Model):
"""
用户共享存储空间可将该用户私有存储共享给 其他用户使用
"""
user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE,
related_name='storage_org_user_id')
to_user_id = models.ForeignKey(to=UserInfo, verbose_name="用户ID", on_delete=models.CASCADE,
related_name='storage_to_user_id', null=True, blank=True)
storage_id = models.ForeignKey(to=AppStorage, verbose_name="存储ID", on_delete=models.CharField)
status_choices = ((1, '共享成功'), (2, '取消共享'))
status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="状态",
help_text="1 共享成功 2 取消共享")
number = models.BigIntegerField(verbose_name="容量大小,单位byte", default=0)
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="添加时间")
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = '私有存储共享'
verbose_name_plural = "私有存储共享"
def __str__(self):
return f"{self.user_id}-{self.to_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
from api.views.storage import StorageView, CleanStorageView, ShareStorageView, StorageConfigView
from api.views.thirdlogin import ValidWxChatToken, ThirdWxAccount
from api.views.uploads import AppAnalyseView, UploadView
@ -48,6 +48,8 @@ urlpatterns = [
re_path("^apps$", AppsView.as_view()),
re_path("^storage$", StorageView.as_view()),
re_path("^storage/clean$", CleanStorageView.as_view()),
re_path("^storage/share$", ShareStorageView.as_view()),
re_path("^storage/config$", StorageConfigView.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()),

@ -9,6 +9,7 @@ import random
from api.models import AppReleaseInfo, Apps
from api.utils.modelutils import get_user_domain_name
from common.base.baseutils import make_app_uuid
from common.base.magic import MagicCacheData
from common.cache.state import MigrateStorageState
from common.utils.caches import del_cache_response_by_short
from common.utils.storage import Storage
@ -78,9 +79,10 @@ def clean_history_apps(app_obj, user_obj, history_release_limit=20):
storage_obj.delete_file(release_obj.release_id, app_obj.type)
storage_obj.delete_file(release_obj.icon_url)
release_obj.delete()
MagicCacheData.invalid_cache(app_obj.app_id)
def save_app_infos(app_file_name, user_obj, app_info, bundle_id, app_img, short, size, issupersign):
def save_app_infos(app_tmp_filename, app_file_name, user_obj, app_info, bundle_id, app_img, short, size, issupersign):
app_uuid = make_app_uuid(user_obj, bundle_id + app_file_name.split(".")[1])
##判断是否存在该app
app_obj = Apps.objects.filter(app_id=app_uuid, user_id=user_obj).first()
@ -123,9 +125,13 @@ def save_app_infos(app_file_name, user_obj, app_info, bundle_id, app_img, short,
app_obj.name = app_info["labelname"]
app_obj.save(update_fields=["name", "bundle_id", "issupersign"])
del_cache_response_by_short(app_obj.app_id)
MagicCacheData.invalid_cache(app_obj.app_id)
AppReleaseInfo.objects.filter(app_id=app_obj).update(is_master=False)
file_info = storage.get_file_info(app_tmp_filename)
logger.info(f'get file {app_tmp_filename} info:{file_info}')
size = file_info.get('content_length', size)
release_data = {
"app_id": app_obj,
"icon_url": app_img,

@ -13,9 +13,11 @@ 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
AppBundleIdBlackList, NotifyReceiver, WeChatInfo, AppDownloadToken, Apps, StorageShareInfo, UserInfo
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
logger = logging.getLogger(__name__)
@ -248,3 +250,47 @@ def check_app_access_token(app_id, access_token, only_check, udid):
return True
if udid:
return AppDownloadToken.objects.filter(app_id__app_id=app_id, bind_udid=udid).count()
@MagicCacheData.make_cache(60 * 60 * 24, key=lambda x: x.app_id)
def get_app_storage_used(app_obj):
binary_size_sum = 0
for release_obj in AppReleaseInfo.objects.filter(app_id=app_obj).all():
if release_obj.release_type == 0:
sign_count = 0
else:
sign_count = AppReleaseInfo.objects.filter(
app_id__apptodeveloper__release_file=release_obj.release_id).values(
'app_id__apptodeveloper__binary_file').distinct().count()
binary_size_sum += release_obj.binary_size * (sign_count + 1)
return binary_size_sum
# @MagicCacheData.make_cache(3600, key=lambda x: x.uid)
def get_user_storage_used(user_obj):
binary_size_sum = 0
if isinstance(user_obj, UserInfo):
user_obj = [user_obj]
for app_obj in Apps.objects.filter(user_id__in=user_obj).all():
binary_size_sum += get_app_storage_used(app_obj)
return binary_size_sum
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
else:
if storage_obj.user_id == user_obj:
max_storage_capacity = storage_obj.max_storage_capacity
storage_capacity = max_storage_capacity if max_storage_capacity else Config.STORAGE_OSS_CAPACITY
else:
share_obj = StorageShareInfo.objects.filter(to_user_id=user_obj, status=1, storage_id=storage_obj).first()
if share_obj:
can_use_number = storage_obj.max_storage_capacity - get_user_storage_used(storage_obj.app_storage.all())
storage_capacity = share_obj.number if share_obj.number < can_use_number else can_use_number
else:
storage_capacity = Config.STORAGE_FREE_CAPACITY
return storage_capacity

@ -2,10 +2,11 @@ import logging
import os
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from api import models
from api.utils.apputils import bytes2human
from api.utils.modelutils import get_user_domain_name, get_app_domain_name, get_app_download_uri
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
@ -62,7 +63,17 @@ class UserInfoSerializer(serializers.ModelSerializer):
model = models.UserInfo
fields = ["username", "uid", "mobile", "job", "email", "domain_name", "role", "first_name",
'head_img', 'storage_active', 'supersign_active', 'free_download_times', 'download_times',
'certification', 'qrcode_domain_name']
'certification', 'qrcode_domain_name', 'storage_used', 'storage_capacity', 'storage_used_capacity']
storage_used_capacity = serializers.SerializerMethodField()
def get_storage_used_capacity(self, obj):
return get_user_storage_used(obj)
storage_used = serializers.SerializerMethodField()
def get_storage_used(self, obj):
return bytes2human(self.get_storage_used_capacity(obj))
head_img = serializers.SerializerMethodField()
@ -128,13 +139,13 @@ class AppsSerializer(serializers.ModelSerializer):
def get_preview_url(self, obj):
return get_app_download_uri(None, obj.user_id, obj)
private_developer_number = serializers.IntegerField(default=0)
count = serializers.IntegerField(default=0)
# private_developer_number = serializers.IntegerField(default=0)
# count = serializers.IntegerField(default=0)
#
# def get_private_developer_number(self, obj):
# return models.AppleDeveloperToAppUse.objects.filter(app_id=obj).count()
#
private_developer_used_number = serializers.IntegerField(default=0)
# private_developer_used_number = serializers.IntegerField(default=0)
#
# def get_private_developer_used_number(self, obj):
# return models.DeveloperDevicesID.objects.filter(app_id=obj,
@ -150,12 +161,12 @@ class AppsSerializer(serializers.ModelSerializer):
def get_sign_type_choice(self, obj):
return get_choices_dict(obj.supersign_type_choices)
supersign_used_number = serializers.IntegerField(default=0)
# supersign_used_number = serializers.IntegerField(default=0)
#
# def get_supersign_used_number(self, obj):
# return models.APPSuperSignUsedInfo.objects.filter(app_id=obj).all().count()
#
developer_used_count = serializers.IntegerField(default=0)
# developer_used_count = serializers.IntegerField(default=0)
#
# def get_developer_used_count(self, obj):
# return models.DeveloperAppID.objects.filter(app_id=obj).all().count()
@ -354,9 +365,35 @@ class StorageSerializer(serializers.ModelSerializer):
model = models.AppStorage
exclude = ["user_id"]
def validate(self, attrs):
endpoint = attrs.get('endpoint', '')
storage_type = attrs.get('storage_type', '')
if storage_type == 2 and endpoint not in Config.STORAGE_ALLOW_ENDPOINT:
raise ValidationError(f'endpoint [{endpoint}] not in {Config.STORAGE_ALLOW_ENDPOINT}')
max_storage_capacity = attrs.get('max_storage_capacity', -1)
if max_storage_capacity != -1:
attrs['max_storage_capacity'] = max_storage_capacity * 1024 * 1024
elif max_storage_capacity == 0:
attrs['max_storage_capacity'] = Config.STORAGE_OSS_CAPACITY
else:
del attrs['max_storage_capacity']
return attrs
storage_type_display = serializers.CharField(source="get_storage_type_display", read_only=True)
download_auth_type_choices = serializers.SerializerMethodField()
used = serializers.SerializerMethodField()
used_number = serializers.SerializerMethodField()
shared = serializers.SerializerMethodField()
def get_used(self, obj):
return obj.app_storage.count()
def get_shared(self, obj):
return models.StorageShareInfo.objects.filter(status=1, storage_id=obj).count()
def get_used_number(self, obj):
return get_user_storage_used(obj.app_storage.all())
# secret_key = serializers.SerializerMethodField()
# 加上此选项,会导致update获取不到值
@ -519,3 +556,27 @@ class PersonalConfigSerializer(serializers.ModelSerializer):
def get_title(self, obj):
return getattr(Config, f'{obj.key}_DES')
class StorageShareSerializer(serializers.ModelSerializer):
class Meta:
model = models.StorageShareInfo
exclude = ["id", "remote_addr", "user_id", "to_user_id"]
target_user = serializers.SerializerMethodField()
cancel = serializers.SerializerMethodField()
used = serializers.SerializerMethodField()
status_display = serializers.CharField(source='get_status_display')
storage_name = serializers.CharField(source='storage_id.name')
def get_target_user(self, obj):
user_obj = obj.user_id
if self.get_cancel(obj):
user_obj = obj.to_user_id
return {'uid': user_obj.uid, 'name': user_obj.first_name}
def get_used(self, obj):
return obj.to_user_id.storage_id == obj.storage_id.id and obj.status == 1
def get_cancel(self, obj):
return self.context.get('user_obj').pk == obj.user_id.pk

@ -120,25 +120,28 @@ def migrating_storage_file_data(user_obj, filename, new_storage_obj, clean_old_d
if old_storage_obj.get_storage_type() == 3:
if new_storage_obj.get_storage_type() == 3:
# 都是本地存储,无需操作
pass
return True
else:
# 本地向云存储上传,并删除本地数据
new_storage_obj.upload_file(local_file_full_path)
res = new_storage_obj.upload_file(local_file_full_path)
if clean_old_data:
delete_local_files(filename)
return delete_local_files(filename)
return res
else:
if new_storage_obj.get_storage_type() == 3:
# 云存储下载 本地,并删除云存储
if download_files_form_oss(old_storage_obj, local_file_full_path, True):
if clean_old_data:
old_storage_obj.delete_file(filename)
return old_storage_obj.delete_file(filename)
return True
else:
# 云存储互传,先下载本地,然后上传新云存储,删除本地和老云存储
if download_files_form_oss(old_storage_obj, local_file_full_path, True):
new_storage_obj.upload_file(local_file_full_path)
delete_local_files(filename)
res1 = new_storage_obj.upload_file(local_file_full_path)
res2 = delete_local_files(filename)
if clean_old_data:
old_storage_obj.delete_file(filename)
return old_storage_obj.delete_file(filename)
return res1 and res2
def migrating_storage_data(user_obj, new_storage_obj, clean_old_data):

@ -14,12 +14,14 @@ from rest_framework.views import APIView
from api.base_views import app_delete
from api.models import Apps, AppReleaseInfo, UserInfo, AppScreenShot, AppDownloadToken
from api.utils.modelutils import get_user_domain_name, get_app_domain_name
from api.utils.apputils import bytes2human
from api.utils.modelutils import get_user_domain_name, get_app_domain_name, get_app_storage_used
from api.utils.response import BaseResponse
from api.utils.serializer import AppsSerializer, AppReleaseSerializer, AppsListSerializer, AppsQrListSerializer, \
AppDownloadTokenSerializer
from api.utils.signalutils import run_delete_app_signal
from api.utils.utils import delete_local_files, delete_app_screenshots_files
from common.base.magic import MagicCacheData
from common.cache.state import MigrateStorageState
from common.core.auth import ExpiringTokenAuthentication
from common.utils.caches import del_cache_response_by_short, get_app_today_download_times, del_cache_by_delete_app
@ -63,7 +65,7 @@ class AppsPageNumber(PageNumberPagination):
page_size = 20 # 每页显示多少条
page_size_query_param = 'size' # URL中每页显示条数的参数
page_query_param = 'page' # URL中页码的参数
max_page_size = None # 最大页码数限制
max_page_size = 100 # 最大页码数限制
class AppsView(APIView):
@ -120,6 +122,7 @@ class AppInfoView(APIView):
if app_obj:
app_serializer = AppsSerializer(app_obj, context={"storage": Storage(request.user)})
res.data = app_serializer.data
res.data['storage_used'] = bytes2human(get_app_storage_used(app_obj))
else:
logger.error(f"app_id:{app_id} is not found in user:{request.user}")
res.msg = "未找到该应用"
@ -268,9 +271,12 @@ class AppReleaseInfoView(APIView):
has_combo.has_combo = None
has_combo.save(update_fields=["has_combo"])
del_cache_response_by_short(has_combo.app_id)
MagicCacheData.invalid_cache(app_obj.app_id)
app_obj.delete()
else:
pass
if app_obj:
MagicCacheData.invalid_cache(app_obj.app_id)
del_cache_response_by_short(app_obj.app_id)
return Response(res.dict)

@ -6,43 +6,101 @@
import logging
from django.db.models import Q
from rest_framework.response import Response
from rest_framework.views import APIView
from api.base_views import storage_change, app_delete
from api.models import AppStorage, Apps
from api.models import AppStorage, Apps, StorageShareInfo, UserInfo
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
from api.utils.serializer import StorageSerializer, StorageShareSerializer
from api.utils.utils import upload_oss_default_head_img
from common.base.baseutils import get_choices_dict
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.core.auth import ExpiringTokenAuthentication, StoragePermission
from common.core.sysconfig import Config
from common.utils.caches import del_cache_storage
logger = logging.getLogger(__name__)
def get_storage_group(request, res, storage_type, is_default=True):
use_storage_obj = request.user.storage
if use_storage_obj:
res.storage = use_storage_obj.id
else:
res.storage = -1 # 默认存储
storage_group = []
if is_default:
storage_group.append({'group_name': '默认存储',
'storages': [{'id': -1, 'name': '默认存储', 'used': True if res.storage == -1 else False}]})
for s_type in storage_type:
s_group = {'group_name': get_choices_name_from_key(AppStorage.storage_choices, s_type), 'storages': []}
for obj in AppStorage.objects.filter(storage_type=s_type).filter(
Q(user_id=request.user) | Q(storageshareinfo__to_user_id=request.user,
storageshareinfo__status=1)).values('id', 'name', 'user_id__first_name',
'user_id__uid').distinct():
if obj['user_id__uid'] != request.user.uid:
ext = {'username': obj['user_id__first_name']}
else:
share_count = StorageShareInfo.objects.filter(storage_id=obj['id'], status=1).count()
share_used = StorageShareInfo.objects.filter(storage_id=obj['id'], status=1,
to_user_id__storage__id=obj['id']).count()
ext = {'share_count': share_count, 'share_used': share_used}
s_info = {'id': obj['id'], 'name': obj['name'], 'used': False, 'ext': ext}
if request.user.storage and request.user.storage.id == obj['id']:
s_info['used'] = True
if not is_default and obj['user_id__uid'] != request.user.uid:
continue
else:
s_group['storages'].append(s_info)
storage_group.append(s_group)
return storage_group
class StorageView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
permission_classes = [StoragePermission, ]
storage_type = [2]
def get(self, request):
res = BaseResponse()
res.storage_list = []
storage_org_list = list(AppStorage.storage_choices)
for storage_t in storage_org_list:
if storage_t[0] not in [0, 3]:
if storage_t[0] in self.storage_type:
res.storage_list.append({'id': storage_t[0], 'name': storage_t[1]})
res.endpoint_list = get_choices_dict([(x, x) for x in Config.STORAGE_ALLOW_ENDPOINT])
act = request.query_params.get("act", None)
is_default = request.query_params.get("is_default", 'true')
pk = request.query_params.get("pk", None)
keysearch = request.query_params.get("keysearch", None)
if act == 'storage_type':
res.download_auth_type_choices = get_choices_dict(AppStorage.download_auth_type_choices)
res.default_max_storage_capacity = Config.STORAGE_OSS_CAPACITY
return Response(res.dict)
if act == 'storage_group':
res.storage_group_list = get_storage_group(request, res, self.storage_type, is_default == 'true')
return Response(res.dict)
# [1,2] 表示七牛存储和阿里云存储
storage_obj = AppStorage.objects.filter(user_id=request.user, storage_type__in=[1, 2])
if storage_obj:
storage_serializer = StorageSerializer(storage_obj, many=True)
storage_queruset = AppStorage.objects.filter(user_id=request.user, storage_type__in=self.storage_type)
if pk:
storage_queruset = storage_queruset.filter(pk=pk)
if keysearch:
storage_queruset = storage_queruset.filter(Q(name__contains=keysearch) | Q(bucket_name=keysearch))
if storage_queruset:
page_obj = PageNumber()
page_serializer = page_obj.paginate_queryset(queryset=storage_queruset.order_by("-created_time"),
request=request,
view=self)
storage_serializer = StorageSerializer(page_serializer, many=True)
storage_data_lists = storage_serializer.data
storage_lists = {}
for storage_data in storage_data_lists:
@ -52,6 +110,9 @@ class StorageView(APIView):
storage_lists[storage_type] = []
storage_lists[storage_type].append(storage_data)
res.data = storage_data_lists
res.count = storage_queruset.count()
else:
res.data = []
use_storage_obj = request.user.storage
if use_storage_obj:
@ -78,11 +139,11 @@ class StorageView(APIView):
res.code = 1005
else:
logger.info(f"user {request.user} add new storage failed")
res.msg = serializer.errors
res.msg = str(serializer.errors)
res.code = 1005
else:
logger.info(f"user {request.user} add new storage failed")
res.msg = serializer.errors
res.msg = str(serializer.errors)
res.code = 1005
return Response(res.dict)
@ -93,6 +154,20 @@ class StorageView(APIView):
use_storage_id = data.get("use_storage_id", None)
force = data.get("force", None)
if use_storage_id:
if use_storage_id != -1:
obj = AppStorage.objects.filter(pk=use_storage_id).first()
if obj:
if obj.user_id != request.user:
if not StorageShareInfo.objects.filter(to_user_id=request.user, status=1,
storage_id=obj).first():
res.code = 1007
res.msg = "数据异常,请重试"
return Response(res.dict)
else:
res.code = 1007
res.msg = "数据异常,请重试"
return Response(res.dict)
with MigrateStorageState(request.user.uid) as state:
if state:
if not storage_change(use_storage_id, request.user, force):
@ -106,10 +181,10 @@ class StorageView(APIView):
storage_id = data.get("id", None)
if storage_id:
if request.user.storage and storage_id == request.user.storage.id:
res.msg = '存储正在使用中,无法修改'
res.code = 1007
return Response(res.dict)
# if request.user.storage and storage_id == request.user.storage.id:
# res.msg = '存储正在使用中,无法修改'
# res.code = 1007
# return Response(res.dict)
storage_obj = AppStorage.objects.filter(id=storage_id, user_id=request.user).first()
storage_obj_bak = AppStorage.objects.filter(id=storage_id, user_id=request.user).first()
serializer = StorageSerializer(instance=storage_obj, data=data, context={'user_obj': request.user},
@ -120,6 +195,10 @@ class StorageView(APIView):
if upload_oss_default_head_img(request.user, new_storage_obj):
res.msg = serializer.validated_data
logger.info(f"user {request.user} update storage success")
del_cache_storage(request.user)
for share_obj in StorageShareInfo.objects.filter(user_id=request.user,
storage_id=new_storage_obj, status=1).all():
del_cache_storage(share_obj.user_id)
else:
storage_obj_bak.save()
logger.error(f"user {request.user} update storage failed")
@ -127,10 +206,10 @@ class StorageView(APIView):
res.code = 1005
else:
logger.info(f"user {request.user} update storage failed")
res.msg = serializer.errors
res.msg = str(serializer.errors)
res.code = 1005
else:
res.msg = serializer.errors
res.msg = str(serializer.errors)
res.code = 1005
else:
res.msg = '该存储不存在'
@ -176,3 +255,164 @@ class CleanStorageView(APIView):
res.code = 1007
res.msg = "参数不合法,清理失败"
return Response(res.dict)
class ShareStorageView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
permission_classes = [StoragePermission, ]
def get(self, request):
res = BaseResponse()
uidsearch = request.query_params.get("uidsearch", None)
status = request.query_params.get("operatestatus", '-1')
share_list = StorageShareInfo.objects.filter(Q(user_id=request.user) | Q(to_user_id=request.user)).distinct()
page_obj = PageNumber()
if uidsearch:
share_list = share_list.filter(
Q(user_id__uid=uidsearch) | Q(to_user_id__uid=uidsearch) | Q(storage_id__name__contains=uidsearch))
try:
if status != '' and get_choices_name_from_key(StorageShareInfo.status_choices, int(status)):
share_list = share_list.filter(status=status)
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})
res.data = app_serializer.data
res.count = share_list.count()
res.status_choices = get_choices_dict(StorageShareInfo.status_choices)
return Response(res.dict)
def post(self, request):
res = BaseResponse()
uid = request.data.get('target_uid')
sid = request.data.get('target_sid')
number = request.data.get('target_number')
if uid and number and sid:
to_user_obj = UserInfo.objects.filter(uid=uid, is_active=True, supersign_active=True).first()
storage_obj = AppStorage.objects.filter(pk=sid, user_id=request.user).first()
if to_user_obj and storage_obj:
if isinstance(number, int) and 0 < number < 999999999:
number = number * 1024 * 1024
max_storage_capacity = storage_obj.max_storage_capacity
storage_capacity = max_storage_capacity if max_storage_capacity else Config.STORAGE_OSS_CAPACITY
number = number if number < storage_capacity else storage_capacity
user_obj = request.user
if user_obj.pk != to_user_obj.pk:
try:
if True:
share_obj = StorageShareInfo.objects.filter(user_id=user_obj, to_user_id=to_user_obj,
status=1, storage_id=storage_obj).first()
if share_obj:
# number += share_obj.number
share_obj.number = number if number < storage_capacity else storage_capacity
share_obj.remote_addr = get_real_ip_address(request)
share_obj.description = f'{user_obj.first_name} 共享给 {to_user_obj.first_name} {share_obj.number} Mb存储空间 '
share_obj.save(update_fields=['number', 'remote_addr', 'description'])
else:
StorageShareInfo.objects.create(user_id=user_obj, to_user_id=to_user_obj,
status=1, number=number,
storage_id=storage_obj,
remote_addr=get_real_ip_address(request),
description=f'{user_obj.first_name} 共享给 {to_user_obj.first_name} {number} Mb存储空间')
return Response(res.dict)
else:
res.msg = f'私有存储空间余额不足,当前存储余额最大为 {all_balance}'
except Exception as e:
res.msg = str(e)
else:
res.msg = '用户不合法'
else:
res.msg = '共享数量异常'
else:
res.msg = '用户信息不存在'
else:
res.msg = '参数有误'
res.code = 1003
return Response(res.dict)
def put(self, request):
res = BaseResponse()
uid = request.data.get('uid')
sid = request.data.get('sid')
if uid:
user_obj = UserInfo.objects.filter(uid=uid, is_active=True, supersign_active=True).first()
if user_obj:
share_obj = StorageShareInfo.objects.filter(user_id=request.user, to_user_id__uid=uid, status=1).all()
info_list = []
number = 0
for obj in share_obj:
storage_obj = obj.storage_id
info_list.append(
{
'storage_name': storage_obj.name, 'number': obj.number,
'storage_id': storage_obj.pk, 'storage_access_key': storage_obj.access_key
})
number += obj.number
res.data = {'uid': user_obj.uid, 'name': user_obj.first_name, "info_list": info_list, "number": number}
else:
res.msg = '用户信息不存在'
res.code = 1003
else:
res.msg = '参数有误'
res.code = 1003
return Response(res.dict)
def delete(self, request):
res = BaseResponse()
uid = request.query_params.get("uid", None)
status = request.query_params.get("status", None)
number = request.query_params.get("number", None)
sid = request.query_params.get("sid", None)
if MigrateStorageState(request.user.uid).get_state():
res.code = 1008
res.msg = "数据迁移中,无法处理该操作"
return Response(res.dict)
if uid and status and number and sid:
share_obj = StorageShareInfo.objects.filter(user_id=request.user, to_user_id__uid=uid, status=status,
number=abs(int(number)), storage_id_id=sid).first()
if share_obj:
target_user_obj = UserInfo.objects.filter(uid=uid).first()
if target_user_obj and target_user_obj.storage:
if target_user_obj.storage.id == share_obj.storage_id.id:
app_obj_lists = Apps.objects.filter(user_id=target_user_obj).all()
for app_obj in app_obj_lists:
res = app_delete(app_obj)
logger.warning(f'clean share storage {target_user_obj} {app_obj} {res}')
target_user_obj.storage = None
target_user_obj.save(update_fields=['storage'])
share_obj.status = 2
share_obj.save(update_fields=['status'])
else:
res.code = 1003
res.msg = '共享记录不存在'
return Response(res.dict)
class StorageConfigView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
permission_classes = [StoragePermission, ]
def get(self, request):
res = BaseResponse()
res.data = {
'user_max_storage_capacity': get_user_storage_capacity(request.user),
'user_used_storage_capacity': get_user_storage_used(request.user),
'user_history_limit': UserInfo.objects.filter(pk=request.user.pk).first().history_release_limit,
}
return Response(res.dict)
def put(self, request):
history_release_limit = request.data.get('user_history_limit')
if history_release_limit:
try:
history_release_limit = int(history_release_limit)
except Exception as e:
logger.warning(f"update user history_release_limit failed Exception:{e}")
history_release_limit = request.user.history_release_limit
UserInfo.objects.filter(pk=request.user.pk).update(history_release_limit=abs(history_release_limit))
return self.get(request)

@ -12,7 +12,8 @@ from rest_framework.views import APIView
from api.models import Apps, AppReleaseInfo, UserInfo, AppScreenShot, CertificationInfo, UserAdDisplayInfo
from api.utils.apputils import get_random_short, save_app_infos
from api.utils.modelutils import get_app_download_uri, check_bundle_id_legal
from api.utils.modelutils import get_app_download_uri, check_bundle_id_legal, get_user_storage_used, \
get_user_storage_capacity
from api.utils.response import BaseResponse
from api.utils.signalutils import run_signal_resign_utils
from common.base.baseutils import make_app_uuid, make_from_user_uuid
@ -21,7 +22,7 @@ from common.core.auth import ExpiringTokenAuthentication
from common.core.sysconfig import Config
from common.utils.caches import upload_file_tmp_name, del_cache_response_by_short
from common.utils.storage import Storage
from common.utils.token import verify_token
from common.utils.token import verify_token, make_token
from fir_ser import settings
logger = logging.getLogger(__name__)
@ -40,6 +41,7 @@ class AppAnalyseView(APIView):
# 1.接收 bundelid ,返回随机应用名称和短连接
bundle_id = request.data.get("bundleid", None)
app_type = request.data.get("type", None)
filesize = request.data.get("filesize", 0)
if bundle_id:
if check_bundle_id_legal(request.user.uid, bundle_id):
res.code = 1004
@ -52,6 +54,17 @@ class AppAnalyseView(APIView):
return Response(res.dict)
if bundle_id and app_type:
storage_used = get_user_storage_used(request.user)
try:
filesize = abs(int(filesize))
except Exception as e:
logger.warning(f"filesize check failed {request.data} Exception:{e}")
filesize = 0
if filesize + storage_used > get_user_storage_capacity(request.user):
res.code = 1008
res.msg = "存储空间不足,请升级存储空间或清理无用的历史版本数据来释放空间"
return Response(res.dict)
ap = 'apk'
if app_type.lower() == 'iOS'.lower():
ap = 'ipa'
@ -94,7 +107,8 @@ class AppAnalyseView(APIView):
"storage": storage_type,
"is_new": is_new,
"binary_url": binary_url,
"enable_sign": enable_sign
"enable_sign": enable_sign,
"access_token": make_token(app_uuid, time_limit=60 * 5, key='update_app_info', force_new=True)
}
if storage_type not in [1, 2]:
res.data['domain_name'] = Config.FILE_UPLOAD_DOMAIN
@ -135,9 +149,21 @@ class AppAnalyseView(APIView):
png_tmp_filename = data.get("png_key")
png_new_filename = data.get("png_key").strip(settings.FILE_UPLOAD_TMP_KEY)
logger.info(f"user {request.user} create or update app {data.get('bundleid')} data:{data}")
if save_app_infos(app_new_filename, request.user, app_info,
data.get("bundleid"), png_new_filename, data.get("short"), data.get('filesize'),
bundle_id = data.get("bundleid", "")
if not bundle_id:
raise KeyError('bundle_id not exist')
app_uuid = make_app_uuid(request.user, bundle_id + app_new_filename.split(".")[1])
if not verify_token(data.get('access_token', ''), app_uuid, False):
res.msg = '授权过期,请重试'
res.code = 1004
storage.delete_file(app_tmp_filename)
storage.delete_file(png_tmp_filename)
return Response(res.dict)
logger.info(f"user {request.user} create or update app {bundle_id} data:{data}")
if save_app_infos(app_tmp_filename, app_new_filename, request.user, app_info,
bundle_id, png_new_filename, data.get("short"), data.get('filesize'),
data.get('enable_sign')):
# 需要将tmp 文件修改为正式文件
storage.rename_file(app_tmp_filename, app_new_filename)
@ -274,7 +300,6 @@ class UploadView(APIView):
res.msg = "文件不存在"
for file_obj in files:
try:
app_type = file_obj.name.split(".")[-1]
if app_type == "tmp":
app_type = file_obj.name.split(".")[-2]
@ -287,6 +312,12 @@ class UploadView(APIView):
res.msg = "错误的类型"
return Response(res.dict)
storage_used = get_user_storage_used(request.user)
if file_obj.size + storage_used > get_user_storage_capacity(request.user):
res.code = 1008
res.msg = "存储空间不足,请升级存储空间或清理无用的历史版本数据来释放空间"
return Response(res.dict)
random_file_name = make_from_user_uuid(request.user.uid)
local_file = os.path.join(settings.MEDIA_ROOT, cert_info.get("upload_key", random_file_name))
# 读取传入的文件

@ -140,9 +140,9 @@ def magic_call_in_times(call_time=24 * 3600, call_limit=6, key=None):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
cache_key = func.__name__
cache_key = f'magic_call_in_times_{func.__name__}'
if key:
cache_key = f'magic_call_in_times_{cache_key}_{key(*args, **kwargs)}'
cache_key = f'{cache_key}_{key(*args, **kwargs)}'
cache_data = cache.get(cache_key)
if cache_data:
if cache_data > call_limit:
@ -169,3 +169,43 @@ def magic_call_in_times(call_time=24 * 3600, call_limit=6, key=None):
return wrapper
return decorator
class MagicCacheData(object):
@staticmethod
def make_cache(cache_time=60 * 10, key=None):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
cache_key = f'magic_cache_data'
if key:
cache_key = f'{cache_key}_{key(*args, **kwargs)}'
else:
cache_key = f'{cache_key}_{func.__name__}'
res = cache.get(cache_key)
if res:
logger.info(f"exec {func} finished. cache_key:{cache_key} cache data exist result:{res}")
return res
else:
start_time = time.time()
try:
res = func(*args, **kwargs)
cache.set(cache_key, res, cache_time)
logger.info(
f"exec {func} finished. time:{time.time() - start_time} cache_key:{cache_key} result:{res}")
except Exception as e:
logger.info(
f"exec {func} failed. time:{time.time() - start_time} cache_key:{cache_key} Exception:{e}")
return res
return wrapper
return decorator
@staticmethod
def invalid_cache(key):
cache_key = f'magic_cache_data_{key}'
res = cache.delete(cache_key)
logger.warning(f"invalid_cache cache_key:{cache_key} result:{res}")

@ -15,7 +15,7 @@ from rest_framework import serializers
from api.models import SystemConfig, UserPersonalConfig
from common.cache.storage import UserSystemConfigCache
from config import BASECONF, THIRDLOGINCONF, AUTHCONF, IPACONF, DOWNLOADTIMESCONF, PAYCONF, STORAGEKEYCONF, SENDERCONF, \
APPLEDEVELOPERCONF, DOMAINCONF, USERPERSONALCONFIGKEY, CONFIGDESCRIPTION
APPLEDEVELOPERCONF, DOMAINCONF, USERPERSONALCONFIGKEY, CONFIGDESCRIPTION, OSSSTORAGECONF
logger = logging.getLogger(__name__)
@ -293,6 +293,27 @@ class UserDownloadTimesCache(ConfigCacheBase):
def AUTH_USER_GIVE_DOWNLOAD_TIMES(self):
return super().get_value('AUTH_USER_GIVE_DOWNLOAD_TIMES', DOWNLOADTIMESCONF.AUTH_USER_GIVE_DOWNLOAD_TIMES)
@property
def SIGN_EXTRA_MULTIPLE(self):
return super().get_value('SIGN_EXTRA_MULTIPLE', DOWNLOADTIMESCONF.SIGN_EXTRA_MULTIPLE)
class OssStorageConfCache(ConfigCacheBase):
def __init__(self, *args, **kwargs):
super(OssStorageConfCache, self).__init__(*args, **kwargs)
@property
def STORAGE_ALLOW_ENDPOINT(self):
return super().get_value('STORAGE_ALLOW_ENDPOINT', OSSSTORAGECONF.STORAGE_ALLOW_ENDPOINT)
@property
def STORAGE_FREE_CAPACITY(self):
return super().get_value('STORAGE_FREE_CAPACITY', OSSSTORAGECONF.STORAGE_FREE_CAPACITY)
@property
def STORAGE_OSS_CAPACITY(self):
return super().get_value('STORAGE_OSS_CAPACITY', OSSSTORAGECONF.STORAGE_OSS_CAPACITY)
class PayConfCache(ConfigCacheBase):
def __init__(self, *args, **kwargs):
@ -417,7 +438,7 @@ class ConfigDescriptionCache(ConfigCacheBase):
class ConfigCache(BaseConfCache, DomainConfCache, IpaConfCache, AuthConfCache, UserDownloadTimesCache, GeeTestConfCache,
PayConfCache, ThirdStorageConfCache, ThirdPartConfCache, AppleDeveloperConfCache,
UserPersonalConfKeyCache, ConfigDescriptionCache, WechatConfCache):
UserPersonalConfKeyCache, ConfigDescriptionCache, WechatConfCache, OssStorageConfCache):
def __init__(self, *args, **kwargs):
super(ConfigCache, self).__init__(*args, **kwargs)

@ -223,6 +223,15 @@ class AliYunOss(object):
os.makedirs(dir_path)
return self.bucket.get_object_to_file(name, local_file_full_path)
def get_file_info(self, name):
result = self.bucket.head_object(name)
base_info = {}
if result.content_length:
base_info['content_length'] = result.content_length
if result.last_modified:
base_info['last_modified'] = result.last_modified
return base_info
def multipart_upload_file(self, local_file_full_path):
if os.path.isfile(local_file_full_path):
total_size = os.path.getsize(local_file_full_path)

@ -72,3 +72,13 @@ class LocalStorage(object):
if os.path.isfile(local_file_full_path):
return True
return False
@staticmethod
def get_file_info(name):
file_path = os.path.join(settings.MEDIA_ROOT, name)
base_info = {}
if os.path.isfile(file_path):
stat_info = os.stat(file_path)
base_info['content_length'] = stat_info.st_size
base_info['last_modified'] = stat_info.st_mtime
return base_info

@ -85,3 +85,13 @@ class QiNiuOss(object):
except Exception as e:
logger.error(f"check download file and move file {local_file_full_path} failed Exception {e}")
return False
def get_file_info(self, name):
bucket = BucketManager(self.qiniu_obj)
result = bucket.stat(self.bucket_name, name)
base_info = {}
if result.get('fsize', 0):
base_info['content_length'] = result.get('fsize', 0)
if result.get('putTime', 0):
base_info['last_modified'] = result.get('putTime', 0) // 10000000
return base_info

@ -42,11 +42,7 @@ def pay_success_notify(user_obj, order_obj):
notify_by_email(user_obj, message_type, get_pay_success_html_content(user_obj, order_obj))
def get_magic_call_key(*args, **kwargs):
return args[0].uid
@magic_call_in_times(key=get_magic_call_key)
@magic_call_in_times(key=lambda *x: x[0].uid)
def sign_failed_notify(user_obj, developer_obj, app_obj):
"""
3, '应用签名失败'
@ -65,7 +61,7 @@ def sign_failed_notify(user_obj, developer_obj, app_obj):
notify_by_email(user_obj, message_type, get_sign_failed_html_content(user_obj, app_obj, developer_obj, now_time))
@magic_call_in_times(key=get_magic_call_key)
@magic_call_in_times(key=lambda *x: x[0].uid)
def sign_unavailable_developer_notify(user_obj, app_obj):
"""
3, '应用签名失败'
@ -82,7 +78,7 @@ def sign_unavailable_developer_notify(user_obj, app_obj):
notify_by_email(user_obj, message_type, get_sign_unavailable_developer_html_content(user_obj, app_obj, now_time))
@magic_call_in_times(key=get_magic_call_key)
@magic_call_in_times(key=lambda *x: x[0].uid)
def sign_app_over_limit_notify(user_obj, app_obj, used_num, limit_number):
"""
0, '签名余额不足'
@ -131,7 +127,7 @@ def check_developer_status_notify(user_obj, developer_obj_list, developer_used_i
yesterday_used_number))
@magic_call_in_times(key=get_magic_call_key)
@magic_call_in_times(key=lambda *x: x[0].uid)
def download_times_not_enough(user_obj, msg):
"""
1, '下载次数不足'

@ -242,7 +242,7 @@ 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)
if consume_user_download_times(user_id, app_obj.app_id, amount, auth_status):
if consume_user_download_times(user_id, app_obj.app_id, int(amount * Config.SIGN_EXTRA_MULTIPLE), auth_status):
return False
return True

@ -110,6 +110,13 @@ class Storage(object):
except Exception as e:
logger.error(f"rename {old_filename} to {new_filename} failed Exception {e}")
def get_file_info(self, name):
if self.storage:
try:
return self.storage.get_file_info(name)
except Exception as e:
logger.error(f"get file info {name} failed Exception {e}")
def upload_file(self, local_file_full_path):
if self.storage:
try:

@ -390,6 +390,7 @@ bIX1aWjPxirQX9mzaL3oEQI=
class DOWNLOADTIMESCONF(object):
SIGN_EXTRA_MULTIPLE = 2 # 超级签名消耗额外倍数,超级签名需要占用的服务大量资源
USER_FREE_DOWNLOAD_TIMES = 5
AUTH_USER_FREE_DOWNLOAD_TIMES = 10
NEW_USER_GIVE_DOWNLOAD_TIMES = 100
@ -459,3 +460,12 @@ class CONFIGDESCRIPTION(object):
class USERPERSONALCONFIGKEY(object):
DEVELOPER_STATUS_CONFIG = ['DEVELOPER_WAIT_ABNORMAL_STATE', 'DEVELOPER_WAIT_ABNORMAL_DEVICE',
'DEVELOPER_ABNORMAL_DEVICE_WRITE']
class OSSSTORAGECONF(object):
STORAGE_FREE_CAPACITY = 2048 * 1024 * 1024 # 单位byte 2G
STORAGE_OSS_CAPACITY = 1024 * 1024 * 1024 * 1024 # 单位byte 1T
STORAGE_ALLOW_ENDPOINT = [
'oss-cn-beijing-internal.aliyuncs.com',
'oss-cn-zhangjiakou-internal.aliyuncs.com'
]

@ -21,7 +21,7 @@ from api.utils.response import BaseResponse
from api.utils.utils import delete_local_files, download_files_form_oss
from common.base.baseutils import file_format_path, delete_app_profile_file, get_profile_full_path, format_apple_date, \
get_format_time, make_app_uuid, make_from_user_uuid, AesBaseCrypt
from common.base.magic import run_function_by_locker, call_function_try_attempts, magic_wrapper
from common.base.magic import run_function_by_locker, call_function_try_attempts, magic_wrapper, MagicCacheData
from common.cache.state import CleanErrorBundleIdSignDataState
from common.cache.storage import RedisCacheBase
from common.constants import DeviceStatus, AppleDeveloperStatus, SignStatus
@ -615,6 +615,7 @@ class IosUtils(object):
sign_status=SignStatus.PROFILE_DOWNLOAD_COMPLETE).update(
sign_status=SignStatus.SIGNATURE_PACKAGE_COMPLETE)
del_cache_response_by_short(app_obj.app_id)
MagicCacheData.invalid_cache(app_obj.app_id)
return True
def check_sign_permission(self):

@ -656,12 +656,17 @@ class DeviceTransferBillView(APIView):
def get(self, request):
res = BaseResponse()
uidsearch = request.query_params.get("uidsearch", None)
status = request.query_params.get("operatestatus", '-1')
user_used_list = IosDeveloperBill.objects.filter(
Q(user_id=request.user) | Q(to_user_id=request.user)).distinct()
page_obj = PageNumber()
if uidsearch:
user_used_list = user_used_list.filter(Q(user_id__uid=uidsearch) | Q(to_user_id__uid=uidsearch))
try:
if status != '' and get_choices_name_from_key(IosDeveloperBill.status_choices, int(status)):
user_used_list = user_used_list.filter(status=status)
except Exception as e:
logger.error(f'status {status} check failed Exception:{e}')
app_page_serializer = page_obj.paginate_queryset(queryset=user_used_list.order_by("-created_time"),
request=request,
@ -674,6 +679,7 @@ class DeviceTransferBillView(APIView):
'used_balance': get_user_public_used_sign_num(request.user),
'all_balance': get_user_public_sign_num(request.user)
}
res.status_choices = get_choices_dict(IosDeveloperBill.status_choices)
return Response(res.dict)
def post(self, request):
@ -726,6 +732,24 @@ class DeviceTransferBillView(APIView):
uid = request.data.get("uid", None)
status = request.data.get("status", None)
number = request.data.get("number", None)
act = request.data.get("act", '')
if act == 'check':
if uid:
user_obj = UserInfo.objects.filter(uid=uid, is_active=True, supersign_active=True).first()
if user_obj:
bill_obj = IosDeveloperBill.objects.filter(user_id=request.user, to_user_id__uid=uid,
status=2).first()
number = 0
if bill_obj:
number = bill_obj.number
res.data = {'uid': user_obj.uid, 'name': user_obj.first_name, "number": number}
else:
res.msg = '用户信息不存在'
res.code = 1003
else:
res.msg = '参数有误'
res.code = 1003
return Response(res.dict)
if uid and status and number:
if check_uid_has_relevant(uid, request.user.uid):

Loading…
Cancel
Save