diff --git a/fir_ser/api/migrations/0018_auto_20200407_1134.py b/fir_ser/api/migrations/0018_auto_20200407_1134.py new file mode 100644 index 0000000..3f8bf3c --- /dev/null +++ b/fir_ser/api/migrations/0018_auto_20200407_1134.py @@ -0,0 +1,28 @@ +# Generated by Django 3.0.3 on 2020-04-07 03:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0017_auto_20200331_1543'), + ] + + operations = [ + migrations.AlterField( + model_name='appreleaseinfo', + name='release_id', + field=models.CharField(db_index=True, max_length=64, unique=True, verbose_name='release 版本id'), + ), + migrations.AlterField( + model_name='apps', + name='app_id', + field=models.CharField(db_index=True, max_length=64, unique=True), + ), + migrations.AlterField( + model_name='apps', + name='short', + field=models.CharField(db_index=True, max_length=16, unique=True, verbose_name='短链接'), + ), + ] diff --git a/fir_ser/api/migrations/0019_auto_20200407_1135.py b/fir_ser/api/migrations/0019_auto_20200407_1135.py new file mode 100644 index 0000000..eee5c64 --- /dev/null +++ b/fir_ser/api/migrations/0019_auto_20200407_1135.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.3 on 2020-04-07 03:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0018_auto_20200407_1134'), + ] + + operations = [ + migrations.AlterField( + model_name='userinfo', + name='uid', + field=models.CharField(db_index=True, max_length=64, unique=True), + ), + ] diff --git a/fir_ser/api/migrations/0020_auto_20200407_1157.py b/fir_ser/api/migrations/0020_auto_20200407_1157.py new file mode 100644 index 0000000..05b1190 --- /dev/null +++ b/fir_ser/api/migrations/0020_auto_20200407_1157.py @@ -0,0 +1,21 @@ +# Generated by Django 3.0.3 on 2020-04-07 03:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0019_auto_20200407_1135'), + ] + + operations = [ + migrations.AddIndex( + model_name='apps', + index=models.Index(fields=['app_id'], name='api_apps_app_id_4c0254_idx'), + ), + migrations.AddIndex( + model_name='apps', + index=models.Index(fields=['id', 'user_id', 'type'], name='api_apps_id_226a6b_idx'), + ), + ] diff --git a/fir_ser/api/models.py b/fir_ser/api/models.py index efa1f85..c9401fc 100644 --- a/fir_ser/api/models.py +++ b/fir_ser/api/models.py @@ -19,7 +19,7 @@ class UserInfo(AbstractUser): blank=True, null=True ) - uid = models.CharField(max_length=64, unique=True) # user_id,唯一标识 + uid = models.CharField(max_length=64, unique=True,db_index=True) # user_id,唯一标识 mobile = models.BigIntegerField(verbose_name="手机", unique=True, help_text="用于手机验证码登录", null=True) qq = models.BigIntegerField(verbose_name="QQ", blank=True, null=True, db_index=True) is_active = models.BooleanField(default=True, verbose_name="账户状态,默认启用") @@ -92,12 +92,12 @@ class VerifyName(models.Model): ######################################## APP表 ######################################## class Apps(models.Model): - app_id = models.CharField(max_length=64, unique=True) # ,唯一标识 + app_id = models.CharField(max_length=64, unique=True,db_index=True) # ,唯一标识 user_id = models.ForeignKey(to="UserInfo",verbose_name="用户ID",on_delete=models.CASCADE) type_choices = ((0, 'android'),(1, 'ios')) type = models.SmallIntegerField(choices=type_choices, default=0, verbose_name="类型") name = models.CharField(max_length=32,blank=True, null=True,verbose_name="应用名称") - short = models.CharField(max_length=16,unique=True,verbose_name="短链接") + short = models.CharField(max_length=16,unique=True,verbose_name="短链接",db_index=True) bundle_id = models.CharField(max_length=64,blank=True,verbose_name="bundle id") has_combo = models.OneToOneField(to="Apps", related_name='combo_app_info', verbose_name="关联应用",on_delete=models.SET_NULL,null=True,blank=True) @@ -109,6 +109,7 @@ class Apps(models.Model): class Meta: verbose_name = '应用信息' verbose_name_plural = "应用信息" + indexes = [models.Index(fields=['app_id']),models.Index(fields=['id','user_id','type'])] def __str__(self): return "%s %s" % (self.name,self.get_type_display()) @@ -116,7 +117,7 @@ class Apps(models.Model): class AppReleaseInfo(models.Model): 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",db_index=True) 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) app_version = models.CharField(max_length=16,verbose_name="app版本",blank=True) diff --git a/fir_ser/api/utils/app/apputils.py b/fir_ser/api/utils/app/apputils.py index 9896bae..b962423 100644 --- a/fir_ser/api/utils/app/apputils.py +++ b/fir_ser/api/utils/app/apputils.py @@ -129,7 +129,7 @@ def SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,short,size): return else: try: - del_cache_response_by_short(appmobj.short) + del_cache_response_by_short(appmobj.short,appmobj.app_id) appmobj.short = short appmobj.name = appinfo["labelname"] appmobj.save() diff --git a/fir_ser/api/utils/storage/storage.py b/fir_ser/api/utils/storage/storage.py index f569a81..241b85f 100644 --- a/fir_ser/api/utils/storage/storage.py +++ b/fir_ser/api/utils/storage/storage.py @@ -105,11 +105,13 @@ class Storage(object): -def del_cache_response_by_short(short): +def del_cache_response_by_short(short,app_id): id = "download" rtn = '_'.join([ id, "short", short ]) - cache.delete(rtn) \ No newline at end of file + cache.delete(rtn) + app_obj_key = "%s_%s" % ('app_instance', app_id) + cache.delete(app_obj_key) \ No newline at end of file diff --git a/fir_ser/api/views/apps.py b/fir_ser/api/views/apps.py index 9b120ea..dbb6294 100644 --- a/fir_ser/api/views/apps.py +++ b/fir_ser/api/views/apps.py @@ -116,11 +116,11 @@ class AppInfoView(APIView): has_combo = apps_obj.has_combo if has_combo: - del_cache_response_by_short(apps_obj.has_combo.short) + del_cache_response_by_short(apps_obj.has_combo.short,apps_obj.has_combo.app_id) apps_obj.has_combo.has_combo = None apps_obj.delete() - del_cache_response_by_short(apps_obj.short) + del_cache_response_by_short(apps_obj.short,apps_obj.app_id) return Response(res.dict) @@ -148,8 +148,8 @@ class AppInfoView(APIView): has_combo.update(**{"has_combo": apps_obj.first()}) else: pass - del_cache_response_by_short(apps_obj.first().short) - del_cache_response_by_short(has_combo.first().short) + del_cache_response_by_short(apps_obj.first().short,apps_obj.first().app_id) + del_cache_response_by_short(has_combo.first().short,has_combo.first().app_id) except Exception as e: res.code = 1004 @@ -158,7 +158,7 @@ class AppInfoView(APIView): try: apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first() apps_obj.description = data.get("description", apps_obj.description) - del_cache_response_by_short(apps_obj.short) + del_cache_response_by_short(apps_obj.short,apps_obj.app_id) apps_obj.short = data.get("short", apps_obj.short) apps_obj.name = data.get("name", apps_obj.name) apps_obj.save() @@ -211,7 +211,7 @@ class AppInfoView(APIView): release_obj.icon_url = release_obj.icon_url.replace(old_file_name.split(".")[0], random_file_name) release_obj.save() - del_cache_response_by_short(apps_obj.short) + del_cache_response_by_short(apps_obj.short,apps_obj.app_id) storage = Storage(request.user) storage.delete_file(old_file_name) @@ -270,7 +270,7 @@ class AppReleaseinfoView(APIView): apps_obj.delete() else: pass - del_cache_response_by_short(apps_obj.short) + del_cache_response_by_short(apps_obj.short,apps_obj.app_id) return Response(res.dict) @@ -306,7 +306,7 @@ class AppReleaseinfoView(APIView): res.msg = "更新失败" return Response(res.dict) - del_cache_response_by_short(apps_obj.short) + del_cache_response_by_short(apps_obj.short,apps_obj.app_id) app_serializer = AppsSerializer(apps_obj) res.data["currentapp"] = app_serializer.data diff --git a/fir_ser/api/views/download.py b/fir_ser/api/views/download.py index f1e2b4e..816a584 100644 --- a/fir_ser/api/views/download.py +++ b/fir_ser/api/views/download.py @@ -11,11 +11,13 @@ from api.utils.TokenManager import DownloadToken from api.utils.app.randomstrings import make_random_uuid from api.utils.app.apputils import make_resigned from api.utils.storage.storage import Storage,LocalStorage -import os +import os,time +from django.core.cache import cache from rest_framework_extensions.cache.decorators import cache_response from api.utils.serializer import AppsSerializer -from api.models import Apps,AppReleaseInfo +from api.models import Apps,AppReleaseInfo,UserInfo +from django.db.models import F from django.http import FileResponse class DownloadView(APIView): ''' @@ -115,31 +117,27 @@ class InstallView(APIView): dtoken = DownloadToken() if dtoken.verify_token(downtoken,release_id): - app_obj = Apps.objects.filter(app_id=app_id,short=short).first() + # app_obj = Apps.objects.filter(app_id=app_id).values("pk",'user_id','type','short').first() + app_obj = self.get_app_instance_by_cache(app_id,900) if app_obj: - app_obj.count_hits = app_obj.count_hits+1 - app_obj.save() - app_obj.user_id.all_download_times = app_obj.user_id.all_download_times+1 - app_obj.user_id.save() - - release_obj = AppReleaseInfo.objects.filter(app_id=app_obj,release_id=release_id).first() - if release_obj: - storage = Storage(app_obj.user_id) - if app_obj.type == 0: - apptype = '.apk' - download_url = storage.get_download_url(release_obj.release_id + apptype,600) + Apps.objects.filter(app_id=app_id).update(count_hits=F('count_hits') + 1) + UserInfo.objects.filter(pk=app_obj.get("user_id")).update(all_download_times=F('all_download_times') + 1) + + # release_obj = AppReleaseInfo.objects.filter(app_id=app_obj.get('pk')).values("release_id") + # if release_obj: + if app_obj.get("type") == 0: + apptype = '.apk' + download_url = self.get_download_url_by_cache(app_obj,release_id + apptype,600) + else: + apptype = '.ipa' + if isdownload : + download_url = self.get_download_url_by_cache(app_obj,release_id + apptype, 600) else: - apptype = '.ipa' - if isdownload : - download_url = storage.get_download_url(release_obj.release_id + apptype, 600) - else: - local_storage = LocalStorage('localhost',False) - # print(local_storage.get_download_url(release_obj.release_id + apptype,600,'plist')) - download_url = local_storage.get_download_url(release_obj.release_id + apptype,600,'plist') - - res.data={"download_url":download_url} - return Response(res.dict) + download_url = self.get_download_url_by_cache(app_obj,release_id + apptype,600,isdownload) + + res.data={"download_url":download_url} + return Response(res.dict) else: res.code = 1004 res.msg = "token校验失败" @@ -148,3 +146,24 @@ class InstallView(APIView): res.code = 1006 res.msg = "该应用不存在" return Response(res.dict) + + def get_download_url_by_cache(self,app_obj,filename,limit,isdownload=False): + now = time.time() + download_val = cache.get("%s_%s" % ('download_url', filename)) + if download_val: + if download_val.get("time") > now - 60: + return download_val.get("download_url") + else: + if isdownload: + local_storage = LocalStorage('localhost', False) + return local_storage.get_download_url(filename, limit, 'plist') + user_obj = UserInfo.objects.filter(pk=app_obj.get("user_id")).first() + storage = Storage(user_obj) + return storage.get_download_url(filename, limit) + + def get_app_instance_by_cache(self,app_id,limit): + app_obj_cache = cache.get("%s_%s" % ('app_instance', app_id)) + if not app_obj_cache: + app_obj_cache = Apps.objects.filter(app_id=app_id).values("pk",'user_id','type').first() + cache.set("%s_%s" % ('app_instance', app_id),app_obj_cache,limit) + return app_obj_cache diff --git a/fir_ser/api/views/uploads.py b/fir_ser/api/views/uploads.py index 1404c13..5419eb9 100644 --- a/fir_ser/api/views/uploads.py +++ b/fir_ser/api/views/uploads.py @@ -231,7 +231,7 @@ class UploadView(APIView): old_file_key = release_obj.icon_url release_obj.icon_url = certinfo.get("upload_key") release_obj.save() - del_cache_response_by_short(app_obj.short) + del_cache_response_by_short(app_obj.short,app_id) storage.delete_file(old_file_key) return Response(res.dict) elif ftype and ftype == 'head':