diff --git a/fir_client/src/components/user/FirSuperSignBase.vue b/fir_client/src/components/user/FirSuperSignBase.vue index ba282ea..e22077c 100644 --- a/fir_client/src/components/user/FirSuperSignBase.vue +++ b/fir_client/src/components/user/FirSuperSignBase.vue @@ -9,6 +9,42 @@ + + 设备共享指的是 将您的设备共享给目标用户使用,目标用户使用的设备数取决于您所共享的最大设备数 + + + + + + + + 查询校验 + + + + + + + + 用户昵称: {{ transferInfo.name }} + 共享设备数: {{ transferInfo.number }} + + + + + + + + + + + + + 确定共享 + 取消 + + @@ -155,12 +191,7 @@ label="客户端IP" prop="remote_addr"> - - + - + +
搜索 -
- 公共签名池设备数量:{{ balance_info.all_balance }} 已经使用:【 {{ - balance_info.used_balance - }} 】 - 还剩:【 {{ balance_info.all_balance - balance_info.used_balance }} 】 可用 - - -
- + + + + + 搜索 + + + 设备共享 + +
+ 共享签名池设备数量:{{ balance_info.all_balance }} 已经使用:【 {{ + balance_info.used_balance + }} 】 + 还剩:【 {{ balance_info.all_balance - balance_info.used_balance }} 】 可用 + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
{ + const loadingobj = this.$loading({ + lock: true, + text: '操作中,请耐心等待', + spinner: 'el-icon-loading', + // background: 'rgba(0, 0, 0, 0.7)' + }); + DeviceTransferBillInfo(data => { + loadingobj.close() + if (data.code === 1000) { + this.$message.success("操作成功") + this.refreshactiveFun() + } else { + this.$message.error("撤回失败 " + data.msg) + } + }, { + 'methods': 'DELETE', + data: {uid: billinfo.target_user.uid, status: billinfo.status, number: billinfo.number} + }) + }).catch(() => { + this.$message({ + type: 'info', + message: '已取消撤回操作' + }); + }); + }, + transferFun(target_uid, target_number) { + DeviceTransferBillInfo(data => { + if (data.code === 1000) { + this.transferVisible = false; + this.$message.success("共享成功") + this.refreshactiveFun() + } else { + this.$message.error("共享失败 " + data.msg) + } + }, {'methods': 'POST', data: {uid: target_uid, number: target_number}}) + }, + checkuid(uid) { + DeviceTransferBillInfo(data => { + if (data.code === 1000) { + this.transferInfo = data.data + this.cantransfer = false + } else { + this.cantransfer = true + this.$message.error("查询失败 " + data.msg) + } + }, {'methods': 'PUT', data: {uid: uid}}) + }, get_developer_uid(uid) { if (uid && uid.indexOf(':') > -1) { return '公共账号池' @@ -1452,10 +1633,7 @@ export default { }, handleCurrentChange(val) { this.pagination.currentPage = val; - this.get_data_from_tabname(this.activeName, { - "size": this.pagination.pagesize, - "page": this.pagination.currentPage - }) + this.this.refreshactiveFun() }, syncdevices() { this.iosdeveloperFun({ @@ -1612,6 +1790,9 @@ export default { this.iosdeveloperFun({"methods": "GET", "data": data}) } else if (tabname === "devicesbill") { this.iosdevicebillFun({"methods": "GET", "data": data}) + } else if (tabname === "transferbill") { + data.uidsearch = this.uidsearch.replace(/^\s+|\s+$/g, ""); + this.deviceTransferFun({"methods": "GET", "data": data}) } else if (tabname === "devicesrank") { if (this.timerangesearch && this.timerangesearch.length === 2) { data.start_time = this.timerangesearch[0]; @@ -1716,10 +1897,7 @@ export default { this.$message.error("操作失败") } if (params.methods === 'PUT' || params.methods === 'DELETE') { - this.get_data_from_tabname(this.activeName, { - "size": this.pagination.pagesize, - "page": this.pagination.currentPage - }) + this.this.refreshactiveFun() } if (params.methods !== 'GET') { this.loadingfun.close(); @@ -1728,6 +1906,35 @@ export default { } }, params) }, + deviceTransferFun(params) { + this.loading = true; + DeviceTransferBillInfo(data => { + if (data.code === 1000) { + this.transfer_bill_lists = data.data; + this.pagination.total = data.count; + if (data.balance_info) { + this.balance_info = data.balance_info; + if (this.balance_info.all_balance - this.balance_info.used_balance === 0) { + if (this.balance_info.all_balance === 0) { + this.bill_percent = 0; + } else { + this.bill_percent = 100; + } + } else { + if (this.balance_info.all_balance - this.balance_info.used_balance < 0) { + this.bill_percent = 100; + } else { + this.bill_percent = parseInt(this.balance_info.used_balance * 100 / this.balance_info.all_balance); + } + } + } + } else { + this.$message.error("操作失败了 " + data.msg); + } + this.loading = false + }, params) + }, + iosdevicebillFun(params) { this.loading = true; DeviceBillInfo(data => { @@ -1738,22 +1945,6 @@ export default { } else { this.app_bill_lists = data.data; this.pagination.total = data.count; - if (data.balance_info) { - this.balance_info = data.balance_info; - if (this.balance_info.all_balance - this.balance_info.used_balance === 0) { - if (this.balance_info.all_balance === 0) { - this.bill_percent = 0; - } else { - this.bill_percent = 100; - } - } else { - if (this.balance_info.all_balance - this.balance_info.used_balance < 0) { - this.bill_percent = 100; - } else { - this.bill_percent = parseInt(this.balance_info.used_balance * 100 / this.balance_info.all_balance); - } - } - } } } else { this.$message.error("操作失败了 " + data.msg); @@ -1824,10 +2015,7 @@ export default { this.developer_udevices_lists = data.data; this.pagination.total = data.count; } else { - this.get_data_from_tabname(this.activeName, { - "size": this.pagination.pagesize, - "page": this.pagination.currentPage - }) + this.this.refreshactiveFun() } } else { this.$message.error("操作失败了 " + data.msg); @@ -1845,7 +2033,7 @@ export default { getUserInfoFun(this); if (this.$route.params.act) { let activeName = this.$route.params.act; - let activeName_list = ["useddevices", "devicesudid", "adddeveloper", "iosdeveloper"]; + let activeName_list = ["iosdeveloper", "adddeveloper","iosudevices","useddevices", "devicesudid","devicesbill","transferbill","devicesrank"]; for (let index in activeName_list) { if (activeName_list[index] === activeName) { this.activeName = activeName; diff --git a/fir_client/src/components/user/FirUserProfileInfo.vue b/fir_client/src/components/user/FirUserProfileInfo.vue index 0963686..68cb3ac 100644 --- a/fir_client/src/components/user/FirUserProfileInfo.vue +++ b/fir_client/src/components/user/FirUserProfileInfo.vue @@ -104,6 +104,13 @@ + + + + + + + diff --git a/fir_client/src/restful/index.js b/fir_client/src/restful/index.js index 1b4b300..c9af06d 100644 --- a/fir_client/src/restful/index.js +++ b/fir_client/src/restful/index.js @@ -753,6 +753,21 @@ export function DeviceBillInfo(callBack, params, load = true) { ); } +/**设备流转账单 */ +export function DeviceTransferBillInfo(callBack, params, load = true) { + getData( + params.methods, + USERSEVER + '/supersign/devicebill', + params.data, + data => { + callBack(data); + }, + load, + true, + true + ); +} + /**签名下载排行 */ export function DeviceRankInfo(callBack, params, load = true) { getData( diff --git a/fir_ser/admin/views/supersign.py b/fir_ser/admin/views/supersign.py index 252a565..ebe3fc9 100644 --- a/fir_ser/admin/views/supersign.py +++ b/fir_ser/admin/views/supersign.py @@ -13,11 +13,11 @@ from rest_framework.views import APIView from admin.utils.serializer import AdminDeveloperSerializer, AdminSuperSignUsedSerializer, AdminBillInfoSerializer from admin.utils.utils import AppsPageNumber, BaseModelSet, ApiResponse -from api.models import APPSuperSignUsedInfo, AppIOSDeveloperInfo, IosDeveloperPublicPoolBill +from api.models import APPSuperSignUsedInfo, AppIOSDeveloperInfo, IosDeveloperPublicPoolBill, IosDeveloperBill from api.utils.auth import AdminTokenAuthentication from api.utils.modelutils import get_user_public_used_sign_num, get_user_public_sign_num, get_user_obj_from_epu from api.utils.utils import get_developer_devices -from common.base.baseutils import get_real_ip_address, get_order_num +from common.base.baseutils import get_real_ip_address logger = logging.getLogger(__name__) @@ -70,7 +70,7 @@ class SuperSignBillFilter(filters.FilterSet): class Meta: model = IosDeveloperPublicPoolBill - fields = ["id", "user_id", "to_user_id", "action", "udid", "app_id"] + fields = ["id", "user_id", "udid", "app_id"] class SuperSignBillView(BaseModelSet): @@ -93,13 +93,10 @@ class SuperSignBillView(BaseModelSet): to_user_obj = get_user_obj_from_epu(to_user_id) if user_obj and to_user_obj and user_obj.pk != to_user_obj.pk: try: - IosDeveloperPublicPoolBill.objects.create(user_id=user_obj, to_user_id=to_user_obj, - action=2, number=number, - remote_addr=get_real_ip_address(request), - product='后台转账', - udid=f'oid:{get_order_num()}', - version=f'{user_obj.first_name} 后台转账 {number} 设备数', - ) + IosDeveloperBill.objects.create(user_id=user_obj, to_user_id=to_user_obj, + status=2, number=number, + remote_addr=get_real_ip_address(request), + description=f'{user_obj.first_name} 共享给 {to_user_obj.first_name} {number} 设备数') return ApiResponse() except Exception as e: msg = str(e) diff --git a/fir_ser/api/migrations/0030_auto_20220203_1545.py b/fir_ser/api/migrations/0030_auto_20220203_1545.py new file mode 100644 index 0000000..5b9b0de --- /dev/null +++ b/fir_ser/api/migrations/0030_auto_20220203_1545.py @@ -0,0 +1,53 @@ +# Generated by Django 3.2.3 on 2022-02-03 15:45 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('api', '0029_auto_20220102_1522'), + ] + + operations = [ + migrations.AlterModelOptions( + name='iosdeveloperpublicpoolbill', + options={'verbose_name': '设备消耗账单', 'verbose_name_plural': '设备消耗账单'}, + ), + migrations.RemoveField( + model_name='iosdeveloperpublicpoolbill', + name='action', + ), + migrations.RemoveField( + model_name='iosdeveloperpublicpoolbill', + name='to_user_id', + ), + migrations.AlterField( + model_name='iosdeveloperpublicpoolbill', + name='user_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, + verbose_name='用户ID'), + ), + migrations.CreateModel( + name='IosDeveloperBill', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status', models.SmallIntegerField(choices=[(0, '失效'), (1, '撤回'), (2, '转账')], default=0, + help_text='0 失效 1 撤回 2 转账', verbose_name='状态')), + ('number', models.IntegerField(default=1, 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='添加时间')), + ('to_user_id', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, + related_name='to_user_id', to=settings.AUTH_USER_MODEL, + verbose_name='用户ID')), + ('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='org_user_id', + to=settings.AUTH_USER_MODEL, verbose_name='用户ID')), + ], + options={ + 'verbose_name': '设备划转账单', + 'verbose_name_plural': '设备划转账单', + }, + ), + ] diff --git a/fir_ser/api/migrations/0031_auto_20220204_0906.py b/fir_ser/api/migrations/0031_auto_20220204_0906.py new file mode 100644 index 0000000..9ad590f --- /dev/null +++ b/fir_ser/api/migrations/0031_auto_20220204_0906.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.3 on 2022-02-04 09:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('api', '0030_auto_20220203_1545'), + ] + + operations = [ + migrations.AddField( + model_name='iosdeveloperbill', + name='updated_time', + field=models.DateTimeField(auto_now=True, verbose_name='更新时间'), + ), + migrations.AlterField( + model_name='iosdeveloperbill', + name='status', + field=models.SmallIntegerField(choices=[(0, '失效'), (1, '已撤回'), (2, '成功')], default=0, + help_text='0 失效 1 撤回 2 转账', verbose_name='状态'), + ), + ] diff --git a/fir_ser/api/models.py b/fir_ser/api/models.py index 1a5fd99..ceaa09c 100644 --- a/fir_ser/api/models.py +++ b/fir_ser/api/models.py @@ -268,9 +268,7 @@ class AppIOSDeveloperInfo(models.Model): user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE) issuer_id = models.CharField(max_length=64, null=False, verbose_name="标识创建认证令牌的发放者") private_key_id = models.CharField(max_length=64, null=False, verbose_name="密钥 ID") - # p8key = models.TextField(max_length=512, null=False, verbose_name="p8key") p8key = AESCharField(max_length=512, null=False, verbose_name="p8key") - # is_actived = models.BooleanField(default=False, verbose_name="是否已经激活") certid = models.CharField(max_length=64, blank=True, verbose_name="超级签名自动创建证书ID", null=True) usable_number = models.IntegerField(verbose_name="可使用设备数", default=100) app_limit_number = models.IntegerField(verbose_name="可分配应用数,最大160", default=100) @@ -546,14 +544,7 @@ class UserAdDisplayInfo(models.Model): class IosDeveloperPublicPoolBill(models.Model): - user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE, - related_name='org_user_id') - to_user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE, - related_name='to_user_id', null=True, blank=True) - - action_choices = ((0, '消费'), (1, '充值'), (2, '转账')) - action = models.SmallIntegerField(choices=action_choices, default=0, verbose_name="资金类型", - help_text="0 消费 1 充值 2 转账") + user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE) number = models.IntegerField(verbose_name="消耗次数", default=1) app_info = models.JSONField(max_length=256, verbose_name="属于哪个APP", null=True, blank=True) udid = models.CharField(max_length=64, verbose_name="设备udid", null=True, blank=True) @@ -568,8 +559,31 @@ class IosDeveloperPublicPoolBill(models.Model): created_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间") class Meta: - verbose_name = '资金流转账单' - verbose_name_plural = "资金流转账单" + verbose_name = '设备消耗账单' + verbose_name_plural = "设备消耗账单" + + def __str__(self): + return "%s-%s" % (self.user_id, self.description) + + +class IosDeveloperBill(models.Model): + user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE, + related_name='org_user_id') + to_user_id = models.ForeignKey(to="UserInfo", verbose_name="用户ID", on_delete=models.CASCADE, + related_name='to_user_id', null=True, blank=True) + + status_choices = ((0, '失效'), (1, '已撤回'), (2, '成功')) + status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="状态", + help_text="0 失效 1 撤回 2 转账") + number = models.IntegerField(verbose_name="设备数量", default=1) + 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 "%s-%s—%s" % (self.user_id, self.to_user_id, self.description) diff --git a/fir_ser/api/urls.py b/fir_ser/api/urls.py index 9511bea..ac2eaef 100644 --- a/fir_ser/api/urls.py +++ b/fir_ser/api/urls.py @@ -27,7 +27,7 @@ from api.views.receiveudids import IosUDIDView, TaskView from api.views.report import ReportView from api.views.storage import StorageView, CleanStorageView from api.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView, SuperSignCertView, \ - DeviceUsedBillView, DeveloperDeviceView, DeviceUsedRankInfoView, AppleDeveloperBindAppsView + DeviceUsedBillView, DeveloperDeviceView, DeviceUsedRankInfoView, AppleDeveloperBindAppsView, DeviceTransferBillView from api.views.thirdlogin import ValidWxChatToken, ThirdWxAccount from api.views.uploads import AppAnalyseView, UploadView @@ -63,6 +63,7 @@ urlpatterns = [ re_path("^supersign/udevices$", DeveloperDeviceView.as_view()), re_path("^supersign/cert$", SuperSignCertView.as_view()), re_path("^supersign/bill$", DeviceUsedBillView.as_view()), + re_path("^supersign/devicebill$", DeviceTransferBillView.as_view()), re_path("^supersign/rank$", DeviceUsedRankInfoView.as_view()), re_path("^supersign/bind$", AppleDeveloperBindAppsView.as_view()), re_path("^package_prices$", PriceView.as_view()), diff --git a/fir_ser/api/utils/app/supersignutils.py b/fir_ser/api/utils/app/supersignutils.py index d8e0417..e1d0651 100644 --- a/fir_ser/api/utils/app/supersignutils.py +++ b/fir_ser/api/utils/app/supersignutils.py @@ -16,7 +16,8 @@ from django.core.cache import cache from django.db.models import Count, F from api.models import APPSuperSignUsedInfo, AppUDID, AppIOSDeveloperInfo, AppReleaseInfo, Apps, APPToDeveloper, \ - UDIDsyncDeveloper, DeveloperAppID, DeveloperDevicesID, IosDeveloperPublicPoolBill, UserInfo, AppleDeveloperToAppUse + UDIDsyncDeveloper, DeveloperAppID, DeveloperDevicesID, IosDeveloperPublicPoolBill, UserInfo, AppleDeveloperToAppUse, \ + IosDeveloperBill from api.utils.app.iossignapi import ResignApp, AppDeveloperApiV2 from api.utils.modelutils import get_ios_developer_public_num, check_ipa_is_latest_sign, \ get_developer_can_used_from_public_sign, update_or_create_developer_udid_info, check_uid_has_relevant @@ -365,7 +366,7 @@ def get_developer_obj_by_others(user_obj, udid, app_obj, read_only): result, is_exist = get_developer_user_by_app_udid([user_obj], udid, app_obj, read_only=read_only) if result: return result - receive_user_id_list = IosDeveloperPublicPoolBill.objects.filter(to_user_id=user_obj).values('user_id').distinct() + receive_user_id_list = IosDeveloperBill.objects.filter(to_user_id=user_obj, status=2).values('user_id').distinct() if receive_user_id_list: result, is_exist = get_developer_user_by_app_udid(UserInfo.objects.filter(pk__in=receive_user_id_list), udid, app_obj, read_only=read_only) @@ -623,7 +624,7 @@ class IosUtils(object): app_info=BillAppInfoSerializer(app_obj).data, developer_info=BillDeveloperInfoSerializer( developer_obj).data, - action=0, number=1, udid_sync_info=udid_sync_info, + number=1, udid_sync_info=udid_sync_info, remote_addr=client_ip, product=udid_sync_info.product, udid=device_udid, version=udid_sync_info.version, app_id=app_obj @@ -921,7 +922,9 @@ class IosUtils(object): for developer_id in developer_id_lists: developer_obj = AppIOSDeveloperInfo.objects.filter(pk=developer_id[0]).first() - if developer_obj and (developer_obj.user_id == app_obj.user_id or check_uid_has_relevant(developer_obj.user_id.uid,app_obj.user_id.uid)): + if developer_obj and ( + developer_obj.user_id == app_obj.user_id or check_uid_has_relevant(developer_obj.user_id.uid, + app_obj.user_id.uid)): IosUtils.clean_super_sign_things_by_app_obj(app_obj, developer_obj) @staticmethod diff --git a/fir_ser/api/utils/modelutils.py b/fir_ser/api/utils/modelutils.py index c878376..4c6d4fb 100644 --- a/fir_ser/api/utils/modelutils.py +++ b/fir_ser/api/utils/modelutils.py @@ -13,7 +13,7 @@ from django.db.models import Count, Sum, Q from rest_framework.pagination import PageNumberPagination from api.models import AppReleaseInfo, UserDomainInfo, DomainCnameInfo, UserAdDisplayInfo, RemoteClientInfo, \ - AppIOSDeveloperInfo, IosDeveloperPublicPoolBill, APPToDeveloper, UserInfo, UDIDsyncDeveloper + AppIOSDeveloperInfo, IosDeveloperPublicPoolBill, APPToDeveloper, UserInfo, UDIDsyncDeveloper, IosDeveloperBill from common.base.baseutils import get_server_domain_from_request, get_user_default_domain_name, get_real_ip_address, \ get_origin_domain_name, is_valid_phone @@ -183,15 +183,14 @@ def get_ios_developer_public_num(user_obj): def get_user_public_sign_num(user_obj): - add_number = IosDeveloperPublicPoolBill.objects.filter(to_user_id=user_obj, action__in=[1, 2]).aggregate( + add_number = IosDeveloperBill.objects.filter(to_user_id=user_obj, status=2).aggregate( number=Sum('number')) number = add_number.get("number", 0) return number if number else 0 def get_user_public_used_sign_num(user_obj): - used_number = IosDeveloperPublicPoolBill.objects.filter(user_id=user_obj, action=0, - udid_sync_info__isnull=False).exclude( + used_number = IosDeveloperPublicPoolBill.objects.filter(user_id=user_obj, udid_sync_info__isnull=False).exclude( udid_sync_info__developerid__user_id=user_obj).values('number', 'udid_sync_info_id').annotate( counts=Count('udid_sync_info_id')).aggregate(number=Sum('number')) @@ -200,13 +199,13 @@ def get_user_public_used_sign_num(user_obj): def get_developer_can_used_from_public_sign(user_obj): - o_number_info = IosDeveloperPublicPoolBill.objects.filter(to_user_id__isnull=False, user_id=user_obj).values( - 'number').aggregate(number=Sum('number')) + o_number_info = IosDeveloperPublicPoolBill.objects.filter(user_id=user_obj).values('number').aggregate( + number=Sum('number')) o_number = o_number_info.get("number", 0) if o_number is None: o_number = 0 u_number_info = IosDeveloperPublicPoolBill.objects.filter( - user_id_id__in=IosDeveloperPublicPoolBill.objects.filter(user_id=user_obj).values('to_user_id_id')).values( + user_id_id__in=IosDeveloperBill.objects.filter(user_id=user_obj).values('to_user_id_id')).values( 'number', 'udid_sync_info_id').annotate( counts=Count('udid_sync_info_id')).aggregate(number=Sum('number')) @@ -260,7 +259,7 @@ def update_or_create_developer_udid_info(device_obj, developer_obj): def check_uid_has_relevant(user_uid, to_user_uid): if user_uid and to_user_uid: - return IosDeveloperPublicPoolBill.objects.filter(user_id__uid=user_uid, to_user_id__uid=to_user_uid).first() + return IosDeveloperBill.objects.filter(user_id__uid=user_uid, to_user_id__uid=to_user_uid, status=2).first() class PageNumber(PageNumberPagination): diff --git a/fir_ser/api/utils/serializer.py b/fir_ser/api/utils/serializer.py index 377475b..2c9ca5a 100644 --- a/fir_ser/api/utils/serializer.py +++ b/fir_ser/api/utils/serializer.py @@ -11,7 +11,7 @@ from api.utils.modelutils import get_user_domain_name, get_app_domain_name, get_ from api.utils.storage.caches import get_user_free_download_times, get_user_cert_auth_status from api.utils.storage.storage import Storage from api.utils.utils import get_developer_udided -from common.base.baseutils import get_choices_dict, get_choices_name_from_key, AppleDeveloperUid +from common.base.baseutils import get_choices_dict, AppleDeveloperUid from common.cache.storage import AdPicShowCache from fir_ser.settings import DEVELOPER_USE_STATUS, DEVELOPER_UID_KEY @@ -642,18 +642,15 @@ class BillDeveloperInfoSerializer(serializers.ModelSerializer): class BillInfoSerializer(serializers.ModelSerializer): class Meta: model = models.IosDeveloperPublicPoolBill - exclude = ["user_id", "to_user_id", "developer_info", "app_info", "udid_sync_info", "app_id"] + exclude = ["user_id", "developer_info", "app_info", "udid_sync_info", "app_id"] app_name = serializers.SerializerMethodField() - action = serializers.SerializerMethodField() description = serializers.SerializerMethodField() is_used = serializers.SerializerMethodField() app_status = serializers.SerializerMethodField() remote_addr = serializers.SerializerMethodField() def get_remote_addr(self, obj): - if obj.to_user_id == self.context.get('user_obj'): - return '' return obj.remote_addr def get_app_status(self, obj): @@ -662,27 +659,43 @@ class BillInfoSerializer(serializers.ModelSerializer): return False def get_is_used(self, obj): - if obj.udid_sync_info or obj.to_user_id: + if obj.udid_sync_info: return True return False - def get_action(self, obj): - return get_choices_name_from_key(obj.action_choices, obj.action) - def get_app_name(self, obj): if obj.app_info: return obj.app_info.get('name') - elif obj.to_user_id: - return obj.user_id.first_name def get_description(self, obj): if obj.udid: - return f"{self.get_app_name(obj)}-{self.get_action(obj)} -{obj.number} 设备数" - if obj.to_user_id: - if obj.to_user_id == self.context.get('user_obj'): - return f"{obj.user_id.first_name}-{self.get_action(obj)} +{obj.number} 设备数" - else: - return f"向 {obj.to_user_id.first_name} {self.get_action(obj)} -{obj.number} 设备数" + return f"{self.get_app_name(obj)}-消耗- {obj.number} 设备数" + + +class BillTransferSerializer(serializers.ModelSerializer): + class Meta: + model = models.IosDeveloperBill + exclude = ["id", "remote_addr", "user_id", "to_user_id"] + + target_user = serializers.SerializerMethodField() + cancel = serializers.SerializerMethodField() + number = serializers.SerializerMethodField() + status_display = serializers.CharField(source='get_status_display') + + 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_cancel(self, obj): + return self.context.get('user_obj').pk == obj.user_id.pk + + def get_number(self, obj): + if self.get_cancel(obj): + return -obj.number + else: + return obj.number class AppReportSerializer(serializers.ModelSerializer): diff --git a/fir_ser/api/views/apps.py b/fir_ser/api/views/apps.py index 8d156c6..b385343 100644 --- a/fir_ser/api/views/apps.py +++ b/fir_ser/api/views/apps.py @@ -236,7 +236,7 @@ class AppInfoView(APIView): # res.code = 1008 # res.msg = "超级签余额不足,无法开启" # return Response(res.dict) - if not check_super_sign_permission(request.user): + if data.get('issupersign', -1) == 1 and not check_super_sign_permission(request.user): logger.error(f"app_id:{app_id} can't open super_sign,owner has no ios developer") res.code = 1008 res.msg = "超级签余额不足,无法开启" diff --git a/fir_ser/api/views/supersign.py b/fir_ser/api/views/supersign.py index b58d1d8..856f5c9 100644 --- a/fir_ser/api/views/supersign.py +++ b/fir_ser/api/views/supersign.py @@ -13,18 +13,20 @@ from rest_framework.response import Response from rest_framework.views import APIView from api.models import AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppUDID, IosDeveloperPublicPoolBill, \ - UDIDsyncDeveloper, AppleDeveloperToAppUse, Apps, DeveloperAppID, APPToDeveloper, DeveloperDevicesID + UDIDsyncDeveloper, AppleDeveloperToAppUse, Apps, DeveloperAppID, APPToDeveloper, DeveloperDevicesID, \ + IosDeveloperBill, UserInfo from api.utils.app.supersignutils import IosUtils from api.utils.auth import ExpiringTokenAuthentication, SuperSignPermission from api.utils.modelutils import get_user_public_used_sign_num, get_user_public_sign_num, PageNumber, \ check_uid_has_relevant from api.utils.response import BaseResponse from api.utils.serializer import DeveloperSerializer, SuperSignUsedSerializer, DeviceUDIDSerializer, BillInfoSerializer, \ - DeveloperDeviceSerializer, AppleDeveloperToAppUseSerializer, AppleDeveloperToAppUseAppsSerializer + DeveloperDeviceSerializer, AppleDeveloperToAppUseSerializer, AppleDeveloperToAppUseAppsSerializer, \ + BillTransferSerializer from api.utils.storage.caches import get_app_download_url from api.utils.utils import get_developer_devices -from common.base.baseutils import get_choices_dict, get_choices_name_from_key, AppleDeveloperUid -from common.cache.state import CleanSignDataState +from common.base.baseutils import get_choices_dict, get_choices_name_from_key, AppleDeveloperUid, get_real_ip_address +from common.cache.state import CleanSignDataState, MigrateStorageState from fir_ser.settings import DEVELOPER_USE_STATUS, DEVELOPER_DISABLED_STATUS, DEVELOPER_UID_KEY logger = logging.getLogger(__name__) @@ -487,11 +489,11 @@ class DeviceUsedBillView(APIView): udid = request.query_params.get("udid", None) act = request.query_params.get("act", None) - receive_user_id_list = IosDeveloperPublicPoolBill.objects.filter(user_id=request.user, - to_user_id__isnull=False).values( + receive_user_id_list = IosDeveloperBill.objects.filter(user_id=request.user, to_user_id__isnull=False, + status=2).values( 'to_user_id').distinct() user_used_list = IosDeveloperPublicPoolBill.objects.filter( - Q(to_user_id=request.user) | Q(user_id=request.user) | Q(user_id_id__in=receive_user_id_list)) + Q(user_id=request.user) | Q(user_id_id__in=receive_user_id_list)) page_obj = PageNumber() if udid: user_used_list = user_used_list.filter(udid=udid) @@ -524,6 +526,126 @@ class DeviceUsedBillView(APIView): return Response(res.dict) +class DeviceTransferBillView(APIView): + authentication_classes = [ExpiringTokenAuthentication, ] + permission_classes = [SuperSignPermission, ] + + def get(self, request): + res = BaseResponse() + uidsearch = request.query_params.get("uidsearch", None) + + 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)) + + app_page_serializer = page_obj.paginate_queryset(queryset=user_used_list.order_by("-created_time"), + request=request, + view=self) + app_serializer = BillTransferSerializer(app_page_serializer, many=True, context={'user_obj': request.user}) + res.data = app_serializer.data + res.count = user_used_list.count() + + res.balance_info = { + 'used_balance': get_user_public_used_sign_num(request.user), + 'all_balance': get_user_public_sign_num(request.user) + } + return Response(res.dict) + + def put(self, request): + res = BaseResponse() + uid = request.data.get('uid') + 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) + + def post(self, request): + res = BaseResponse() + uid = request.data.get('uid') + number = request.data.get('number') + if uid and number: + to_user_obj = UserInfo.objects.filter(uid=uid, is_active=True, supersign_active=True).first() + if to_user_obj: + if isinstance(number, int) and 0 < number < 99999: + user_obj = request.user + if user_obj.pk != to_user_obj.pk: + try: + use_num = get_developer_devices(AppIOSDeveloperInfo.objects.filter(user_id=user_obj)) + all_balance = use_num.get('max_total', 0) + if all_balance > 0 and number <= all_balance: + bill_obj = IosDeveloperBill.objects.filter(user_id=user_obj, to_user_id=to_user_obj, + status=2).first() + if bill_obj: + bill_obj.number = number + bill_obj.number + if bill_obj.number >= all_balance: + bill_obj.number = all_balance + bill_obj.remote_addr = get_real_ip_address(request) + bill_obj.description = f'{user_obj.first_name} 共享给 {to_user_obj.first_name} {bill_obj.number} 设备数' + bill_obj.save(update_fields=['number', 'remote_addr', 'description']) + else: + IosDeveloperBill.objects.create(user_id=user_obj, to_user_id=to_user_obj, + status=2, number=number, + remote_addr=get_real_ip_address(request), + description=f'{user_obj.first_name} 共享给 {to_user_obj.first_name} {number} 设备数') + 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 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) + if MigrateStorageState(request.user.uid).get_state(): + res.code = 1008 + res.msg = "数据迁移中,无法处理该操作" + return Response(res.dict) + if uid and status and number: + bill_obj = IosDeveloperBill.objects.filter(user_id=request.user, to_user_id__uid=uid, status=status, + number=abs(int(number))).first() + if bill_obj: + target_user_obj = UserInfo.objects.filter(uid=uid).first() + bill_obj.status = 1 + bill_obj.save(update_fields=['status']) + if target_user_obj: + for app_obj in Apps.objects.filter(user_id=target_user_obj, type=1): + app_obj.issupersign = False + app_obj.save(update_fields=['issupersign']) + count = APPToDeveloper.objects.filter(app_id=app_obj).count() + if app_obj.issupersign or count > 0: + logger.info(f"app_id:{app_obj} is super_sign ,clean IOS developer") + IosUtils.clean_app_by_user_obj(app_obj) + else: + res.code = 1003 + res.msg = '转移记录不存在' + return Response(res.dict) + + class DeviceUsedRankInfoView(APIView): authentication_classes = [ExpiringTokenAuthentication, ] permission_classes = [SuperSignPermission, ] @@ -534,8 +656,8 @@ class DeviceUsedRankInfoView(APIView): search_key = request.query_params.get("appnamesearch") start_time = request.query_params.get("start_time") end_time = request.query_params.get("end_time") - receive_user_id_list = IosDeveloperPublicPoolBill.objects.filter(user_id=request.user, - to_user_id__isnull=False).values( + receive_user_id_list = IosDeveloperBill.objects.filter(user_id=request.user, to_user_id__isnull=False, + status=2).values( 'to_user_id').distinct() app_used_sign_objs = APPSuperSignUsedInfo.objects.filter( Q(user_id=request.user) | Q(user_id_id__in=receive_user_id_list))