设备共享逻辑及前端页面书写

dependabot/npm_and_yarn/fir_admin/url-parse-1.5.10
nineven 3 years ago
parent 10ab90a104
commit 614db12422
  1. 312
      fir_client/src/components/user/FirSuperSignBase.vue
  2. 7
      fir_client/src/components/user/FirUserProfileInfo.vue
  3. 15
      fir_client/src/restful/index.js
  4. 17
      fir_ser/admin/views/supersign.py
  5. 53
      fir_ser/api/migrations/0030_auto_20220203_1545.py
  6. 23
      fir_ser/api/migrations/0031_auto_20220204_0906.py
  7. 38
      fir_ser/api/models.py
  8. 3
      fir_ser/api/urls.py
  9. 11
      fir_ser/api/utils/app/supersignutils.py
  10. 15
      fir_ser/api/utils/modelutils.py
  11. 47
      fir_ser/api/utils/serializer.py
  12. 2
      fir_ser/api/views/apps.py
  13. 140
      fir_ser/api/views/supersign.py

@ -9,6 +9,42 @@
<apple-developer-bind-app v-if="bind_appletoapp_sure" :issuer_id="editdeveloperinfo.issuer_id"
transitionName="bind_appletoapp"/>
</el-dialog>
<el-dialog :close-on-click-modal="false" :destroy-on-close="true" :visible.sync="transferVisible"
style="text-align:center" title="设备共享" width="700px">
<el-tag style="margin: 0 auto 20px">设备共享指的是 将您的设备共享给目标用户使用目标用户使用的设备数取决于您所共享的最大设备数</el-tag>
<el-form label-width="30%">
<el-form-item label="目标用户UID" style="width: 80%">
<el-row :gutter="24">
<el-col :span="19">
<el-input v-model="target_uid" clearable/>
</el-col>
<el-col :span="4">
<el-button @click="checkuid(target_uid)">查询校验</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item v-if="transferInfo.uid" label="目标用户信息" style="width: 80%;text-align: left">
<el-row :gutter="24">
<el-col :span="18">
<!-- <el-tag> 用户UID: {{transferInfo.uid}}</el-tag>-->
<el-tag> 用户昵称: {{ transferInfo.name }}</el-tag>
<el-tag> 共享设备数: {{ transferInfo.number }}</el-tag>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="共享设备数量" style="width: 80%;text-align: left">
<el-row :gutter="24">
<el-col :span="18">
<el-input-number v-model="target_number" :max="999999" :min="1"/>
</el-col>
</el-row>
</el-form-item>
<el-button :disabled="cantransfer" @click="transferFun(target_uid,target_number)">确定共享</el-button>
<el-button @click="transferVisible=false">取消</el-button>
</el-form>
</el-dialog>
<el-dialog :close-on-click-modal="false" :destroy-on-close="true" :visible.sync="importcertDeveloperVisible"
style="text-align:center" title="发布证书导入" width="700px">
<el-form label-width="30%">
@ -155,12 +191,7 @@
label="客户端IP"
prop="remote_addr">
</el-table-column>
<el-table-column
align="center"
label="账单类型"
prop="action"
width="100">
</el-table-column>
<el-table-column
:formatter="deviceformatter"
@ -174,28 +205,28 @@
label="应用状态"
>
<template slot-scope="scope">
<div v-if="scope.row.action==='消费'">
<el-tag v-if="scope.row.app_status===false" type="info">应用删除</el-tag>
<el-tag v-else>
应用存在
</el-tag>
</div>
<span v-else>{{ scope.row.description }}</span>
<el-tag v-if="scope.row.app_status===false" type="info">应用删除</el-tag>
<el-tag v-else>
应用存在
</el-tag>
</template>
</el-table-column>
<el-table-column
align="center"
label="设备状态">
<template slot-scope="scope">
<div v-if="scope.row.action==='消费'">
<el-tag v-if="scope.row.is_used===false" type="info">已经释放</el-tag>
<el-tag v-else>
使用中
</el-tag>
</div>
<el-tag v-if="scope.row.is_used===false" type="info">已经释放</el-tag>
<el-tag v-else>
使用中
</el-tag>
</template>
</el-table-column>
<el-table-column
align="center"
label="账单备注"
prop="description"
width="100">
</el-table-column>
</el-table>
<div style="margin-top: 20px">
<el-pagination
@ -962,19 +993,6 @@
<el-button icon="el-icon-search" type="primary" @click="handleCurrentChange(1)">
搜索
</el-button>
<div style="width: 40%;margin-right: 30px;float:right">
<el-link :underline="false">公共签名池设备数量{{ balance_info.all_balance }} 已经使用 {{
balance_info.used_balance
}}
还剩 {{ balance_info.all_balance - balance_info.used_balance }} 可用
</el-link>
<el-progress
:color="developer_usedColor"
:percentage="bill_percent"
:stroke-width="18" :text-inside="true" status="success"
type="line"/>
</div>
<el-table
v-loading="loading"
:data="app_bill_lists"
@ -1035,6 +1053,101 @@
</el-table>
</el-tab-pane>
<el-tab-pane label="设备共享记录" name="transferbill">
<el-input
v-model="uidsearch"
clearable
placeholder="输入用户UID"
style="width: 30%;margin-right: 30px;margin-bottom: 10px"/>
<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">
<el-link :underline="false">共享签名池设备数量{{ balance_info.all_balance }} 已经使用 {{
balance_info.used_balance
}}
还剩 {{ balance_info.all_balance - balance_info.used_balance }} 可用
</el-link>
<el-progress
:color="developer_usedColor"
:percentage="bill_percent"
:stroke-width="18" :text-inside="true" status="success"
type="line"/>
</div>
<el-table
v-loading="loading"
:data="transfer_bill_lists"
border
stripe
style="width: 100%">
<el-table-column
align="center"
fixed
label="目标用户UID">
<template slot-scope="scope">
<el-popover placement="top" trigger="hover">
<p>用户UID: {{ scope.row.target_user.uid }}</p>
<p>用户昵称: {{ scope.row.target_user.name }}</p>
<div slot="reference" class="name-wrapper">
<span>{{ scope.row.target_user.uid }}</span>
</div>
</el-popover>
</template>
</el-table-column>
<el-table-column
align="center"
label="状态"
prop="status_display"
width="100">
</el-table-column>
<el-table-column
align="center"
label="设备数量"
prop="number"
width="100">
</el-table-column>
<el-table-column
:formatter="deviceformatter"
align="center"
label="日期"
prop="created_time"
width="160">
</el-table-column>
<el-table-column
align="center"
label="备注"
prop="description">
</el-table-column>
<el-table-column
align="center"
fixed="right"
label="操作"
width="100">
<template slot-scope="scope">
<el-tooltip v-if="scope.row.cancel" content="点击撤回共享设备数,并清理目标用户签名脏数据" placement="top">
<el-tag v-if="scope.row.status===2"
type="success"
@click="cancelshare(scope.row)"
>成功
</el-tag>
<el-tag v-else-if="scope.row.status === 1">已撤回</el-tag>
<el-tag v-else>状态异常</el-tag>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="设备消耗统计" name="devicesrank">
<el-input
v-model="appnamesearch"
@ -1118,6 +1231,7 @@ import {
developercert,
DeviceBillInfo,
DeviceRankInfo,
DeviceTransferBillInfo,
iosdeveloper,
iosdevices,
iosdevicesudid,
@ -1140,6 +1254,7 @@ export default {
app_developer_lists: [],
app_devices_lists: [],
app_bill_lists: [],
transfer_bill_lists: [],
app_rank_lists: [],
app_rank_number: 0,
app_bill_info_lists: [],
@ -1148,10 +1263,16 @@ export default {
udidsearch: "",
Bundleidsearch: "",
appidseach: "",
uidsearch: "",
appnamesearch: "",
timerangesearch: [],
dialogaddDeveloperVisible: false,
importcertDeveloperVisible: false,
target_uid: '',
target_number: 1,
transferVisible: false,
cantransfer: true,
transferInfo: {uid: '', name: '', number: 0},
title: "",
editdeveloperinfo: {auth_type: 0, usable_number: 100, app_limit_number: 100, p8key: ''},
isedit: false,
@ -1228,6 +1349,66 @@ export default {
}
},
methods: {
refreshactiveFun() {
this.get_data_from_tabname(this.activeName, {
"size": this.pagination.pagesize,
"page": this.pagination.currentPage
})
},
cancelshare(billinfo) {
this.$confirm('确定要撤回么,撤回之后,对方账号将无法使用该账户设备数,并且会清理对方账户已经设备的设备数据信息?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
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;

@ -104,6 +104,13 @@
</el-form-item>
<el-form-item label="用户UID">
<el-row :gutter="36">
<el-col :span="18">
<el-input :value="userinfo.uid" disabled/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="手机号码">
<el-row :gutter="36">
<el-col :span="16">

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

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

@ -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': '设备划转账单',
},
),
]

@ -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='状态'),
),
]

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

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

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

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

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

@ -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 = "超级签余额不足,无法开启"

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

Loading…
Cancel
Save