增加应用专属开发者功能

qrnn
youngS 3 years ago
parent 4decc14bf8
commit d3fc7327a6
  1. 27
      fir_client/src/components/apps/FirAppInfossupersign.vue
  2. 260
      fir_client/src/components/base/AppleDeveloperBindApp.vue
  3. 113
      fir_client/src/components/user/FirSuperSignBase.vue
  4. 2
      fir_client/src/main.js
  5. 15
      fir_client/src/restful/index.js
  6. 2
      fir_client/vue.config.js
  7. 30
      fir_ser/api/migrations/0026_appledevelopertoappuse.py
  8. 16
      fir_ser/api/models.py
  9. 3
      fir_ser/api/urls.py
  10. 38
      fir_ser/api/utils/app/supersignutils.py
  11. 74
      fir_ser/api/utils/serializer.py
  12. 128
      fir_ser/api/views/supersign.py

@ -2,6 +2,15 @@
<div style="margin-top: 20px;width: 66%;margin-left: 8%"> <div style="margin-top: 20px;width: 66%;margin-left: 8%">
<el-dialog
:close-on-click-modal="false"
:close-on-press-escape="false"
:title="appletoapp_title"
:visible.sync="bind_appletoapp_sure"
width="1166px">
<apple-developer-bind-app v-if="bind_appletoapp_sure" :app_id="currentapp.app_id"
transitionName="bind_appletoapp_app"/>
</el-dialog>
<el-form v-if="currentapp.type === 1 && $store.state.userinfo.supersign_active" label-width="80px"> <el-form v-if="currentapp.type === 1 && $store.state.userinfo.supersign_active" label-width="80px">
@ -24,7 +33,12 @@
<el-link v-else :underline="false" style="margin-left: 20px">超级签名iOS专用需要配置好苹果开发者账户方可开启</el-link> <el-link v-else :underline="false" style="margin-left: 20px">超级签名iOS专用需要配置好苹果开发者账户方可开启</el-link>
</el-form-item> </el-form-item>
<el-form-item label="专属配置" label-width="200px">
<el-button :disabled="supersign_disable" @click="bindAppletoapp">配置专属苹果开发账户</el-button>
<el-link :underline="false" style="margin-left: 20px" @click="bindAppletoapp"> 拥有
{{ currentapp.private_developer_number }} 个专属苹果开发者
</el-link>
</el-form-item>
<el-form-item label="签名限额" label-width="200px"> <el-form-item label="签名限额" label-width="200px">
@ -91,9 +105,11 @@
<script> <script>
import {apputils,} from "@/restful" import {apputils,} from "@/restful"
import {deepCopy} from "@/utils"; import {deepCopy} from "@/utils";
import AppleDeveloperBindApp from "@/components/base/AppleDeveloperBindApp";
export default { export default {
name: "FirAppInfossupersign", name: "FirAppInfossupersign",
components: {AppleDeveloperBindApp},
data() { data() {
return { return {
currentapp: {}, currentapp: {},
@ -104,11 +120,16 @@ export default {
sign_type_list: [], sign_type_list: [],
sign_type: 0, sign_type: 0,
defualt_dtitle: '', defualt_dtitle: '',
defualt_dtitle_name: '' defualt_dtitle_name: '',
bind_appletoapp_sure: false,
appletoapp_title: ''
} }
}, },
methods: { methods: {
bindAppletoapp() {
this.appletoapp_title = "专属签名开发者信息 " + this.currentapp.name + "-" + this.currentapp.bundle_id;
this.bind_appletoapp_sure = true;
},
set_default_flag() { set_default_flag() {
this.showsupersignflag = false; this.showsupersignflag = false;
}, },

@ -0,0 +1,260 @@
<template>
<transition :name="transitionName">
<div>
<div style="text-align: center">
<el-transfer
v-model="choices_data"
:button-texts="['', '']"
:data="app_developer_lists"
:format="{
noChecked: '${total}',
hasChecked: '${checked}/${total}'
}"
:left-default-checked="left_data"
:props="s_props"
:right-default-checked="right_data"
:titles="s_titles"
filterable
style="text-align: left; display: inline-block"
@change="handleChange">
<div slot-scope="{ option }" style="height: 60px;display: block">
<el-popover v-if="app_id" placement="left" trigger="hover">
<el-tag v-if="option.app_private_number > 0">专属应用账户存在其他应用单独使用该开发者</el-tag>
<el-tag v-else type="info">公共应用账户所有应用公用此开发者</el-tag>
<p>开发者ID: {{ option.issuer_id }} </p>
<p>应用签名数: {{ option.app_used_number }} </p>
<p>已分配专属应用数: {{ option.app_private_number }} </p>
<p>开发者账户已使用设备数: {{ option.developer_used_number }}</p>
<p>开发者账户可用设备数: {{ 100 - option.developer_used_number }}</p>
<p>由于您设置可用设备数: {{ option.usable_number }} ,所以现在可用设备数: {{
option.usable_number - option.developer_used_number > 0
? option.usable_number - option.developer_used_number : 0
}}</p>
<p>证书到期时间: {{ option.cert_expire_time }}</p>
<p v-if="option.app_usable_number > 0">
分配数量:
<el-input-number v-model="option.app_usable_number" :max="100" :min="1"
label="分配数量" size="small"/>
<el-button size="small" style="margin-left: 10px" @click="saveNumber(option)">保存修改</el-button>
</p>
<p>描述: {{ option.description }}</p>
<div slot="reference" class="name-wrapper">
<div>{{ option.issuer_id }} - {{
option.usable_number - option.developer_used_number > 0
? option.usable_number - option.developer_used_number : 0
}} - {{ option.description }}
</div>
</div>
</el-popover>
<el-popover v-if="issuer_id" placement="left" trigger="hover">
<p>应用ID: {{ option.app_id }} </p>
<p>应用名称: {{ option.name }} </p>
<p>应用BundleId: {{ option.bundle_id }} </p>
<p>短连接: {{ option.short }} </p>
<p v-if="option.app_usable_number>0">
分配数量:
<el-input-number v-model="option.app_usable_number" :max="100" :min="1"
label="分配数量" size="small"/>
<el-button size="small" style="margin-left: 10px" @click="saveNumber(option)">保存修改</el-button>
</p>
<p>使用数量: {{ option.app_used_number }}</p>
<p>描述: {{ option.description }}</p>
<div slot="reference" class="name-wrapper">
<div><span v-if="option.app_usable_number>0">{{ option.app_usable_number }}</span> -{{ option.name }} -
{{ option.bundle_id }} - {{ option.app_id }}
</div>
</div>
</el-popover>
</div>
<el-button slot="left-footer" class="transfer-footer" @click="getinfos">刷新</el-button>
<el-button slot="right-footer" class="transfer-footer" @click="saveAppleApps">保存</el-button>
<el-link v-if="issuer_id" slot="right-footer" :underline="false" style="margin-left: 20px">已经分配
{{ app_private_used_number }} 还有 {{ 100 - app_private_used_number }} 可分配使用
</el-link>
</el-transfer>
</div>
</div>
</transition>
</template>
<script>
import {developerBindAppFun, iosdeveloper} from "@/restful";
export default {
name: 'AppleDeveloperBindApp',
props: {
transitionName: {
type: String,
default: 'appledeveloperbind'
},
app_id: {
type: String,
default: undefined
},
issuer_id: {
type: String,
default: undefined
},
},
data() {
return {
app_developer_lists: [],
left_data: [],
right_data: [],
choices_data: [],
choices_data_list: [],
s_props: {},
s_titles: [],
app_private_used_number: 100
};
},
mounted() {
this.getinfos()
},
methods: {
get_choice_data_from_key(key_list) {
let n_data = [];
let field = 'app_id'
if (this.app_id) {
field = 'issuer_id'
}
this.app_private_used_number = 0
for (let i = 0; i < this.app_developer_lists.length; i++) {
for (let j = 0; j < key_list.length; j++) {
if (key_list[j] === this.app_developer_lists[i][field]) {
n_data.push(this.app_developer_lists[i])
this.app_private_used_number += this.app_developer_lists[i]['app_usable_number']
}
}
}
return [n_data, this.app_private_used_number]
},
handleChange(value,) {
this.get_choice_data_from_key(value)
},
saveNumber(infos) {
if (this.get_choice_data_from_key(this.choices_data)[1] > 100) {
this.$message.warning("超出最大分配额度,请注意,尽量保证所有应用分配额度总和为 100")
}
developerBindAppFun(data => {
if (data.code === 1000) {
// this.$message.success("")
// this.choices_data = this.format_data(data.data)
} else {
this.$message.error("数据更新失败" + data.msg)
}
this.getinfos()
}, {
methods: 'PUT',
data: {'app_id': this.app_id, 'issuer_id': this.issuer_id, 'infos': infos}
})
},
getinfos() {
if (this.issuer_id) {
this.s_props = {
key: 'app_id',
label: 'name',
}
this.s_titles = ['可分配苹果应用', '已分配苹果应用']
this.getBindInfo({act: 'apps'})
} else {
this.s_props = {
key: 'issuer_id',
label: 'issuer_id',
disabled: 'is_disabled'
}
this.s_titles = ['可分配签名开发者账户', '已分配签名开发者账户']
this.iosdeveloperFun()
}
this.getBindInfo({})
},
saveAppleApps() {
developerBindAppFun(data => {
if (data.code === 1000) {
// this.$message.success("")
// this.choices_data = this.format_data(data.data)
} else {
this.$message.error("数据更新失败" + data.msg)
}
this.getinfos()
}, {
methods: 'POST',
data: {'app_id': this.app_id, 'issuer_id': this.issuer_id, 'choices_data': this.choices_data}
})
},
iosdeveloperFun() {
this.loadingfun = this.$loading({
lock: true,
text: '执行中,请耐心等待...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
iosdeveloper(data => {
if (data.code === 1000) {
this.app_developer_lists = data.data;
}
this.loadingfun.close()
}
,
{"methods": 'GET', "data": {app_id: this.app_id, issuer_id: this.issuer_id}})
},
getBindInfo(params) {
if (this.issuer_id) {
params.issuer_id = this.issuer_id
}
if (this.app_id) {
params.app_id = this.app_id
}
developerBindAppFun(data => {
if (data.code === 1000) {
if (params.act === 'apps') {
this.app_developer_lists = data.data
this.get_choice_data_from_key(this.choices_data)
} else {
this.choices_data_list = data.data;
this.choices_data = this.format_data(data.data)
this.get_choice_data_from_key(this.choices_data)
this.$message.success("数据获取成功")
}
} else {
this.$message.error("数据获取失败" + data.msg)
}
}, {methods: 'GET', data: params})
},
format_data(data) {
let n_data = [];
let field = 'app_id'
if (this.app_id) {
field = 'issuer_id'
}
this.app_private_used_number = 0
if (data && data.length > 0) {
for (let i = 0; i < data.length; i++) {
n_data.push(data[i][field])
}
}
return n_data
},
format_data_number() {
},
}
}
</script>
<style scoped>
/deep/ .el-transfer-panel {
width: 460px;
}
/*/deep/ .el-transfer-panel__item {*/
/* height: 60px;*/
/*}*/
.transfer-footer {
text-align: center;
float: right;
}
</style>

@ -1,6 +1,14 @@
<template> <template>
<el-main> <el-main>
<el-dialog
:close-on-click-modal="false"
:close-on-press-escape="false"
:title="appletoapp_title"
:visible.sync="bind_appletoapp_sure"
width="1166px">
<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="importcertDeveloperVisible" <el-dialog :close-on-click-modal="false" :destroy-on-close="true" :visible.sync="importcertDeveloperVisible"
style="text-align:center" title="发布证书导入" width="700px"> style="text-align:center" title="发布证书导入" width="700px">
<el-form label-width="30%"> <el-form label-width="30%">
@ -85,7 +93,7 @@
<el-button v-if="isedit && editdeveloperinfo.is_actived" size="small" type="success" <el-button v-if="isedit && editdeveloperinfo.is_actived" size="small" type="success"
@click="activedeveloperFun(editdeveloperinfo,'checkauth')">账户激活检测 @click="activedeveloperFun(editdeveloperinfo,'checkauth')">账户激活检测
</el-button> </el-button>
<!-- <el-button v-if="isedit && editdeveloperinfo.is_actived" size="small" @click="bindAppletoapp(editdeveloperinfo)">专属应用</el-button>-->
<el-button @click="updateorcreate">保存</el-button> <el-button @click="updateorcreate">保存</el-button>
<el-button @click="canceledit">取消</el-button> <el-button @click="canceledit">取消</el-button>
</div> </div>
@ -178,12 +186,22 @@
<el-input <el-input
v-model="appidseach" v-model="appidseach"
clearable clearable
placeholder="输入开发者用户ID" placeholder="输入开发者ID"
style="width: 30%;margin-right: 30px;margin-bottom: 10px"/> style="width: 27%;margin-right: 10px;margin-bottom: 10px"/>
<el-select v-model="developer_choice" clearable placeholder="账户类型"
style="width: 12%;margin-right: 10px;margin-bottom: 10px">
<el-option
v-for="item in developer_options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<el-button icon="el-icon-search" type="primary" @click="handleCurrentChange(1)"> <el-button icon="el-icon-search" type="primary" @click="handleCurrentChange(1)">
搜索 搜索
</el-button> </el-button>
<div style="width: 43%;margin-right: 30px;float:right"> <div style="width: 45%;margin-right: 30px;float:right">
<el-link :underline="false">私有总设备量{{ developer_used_info.all_usable_number }} 已经使用平台{{ <el-link :underline="false">私有总设备量{{ developer_used_info.all_usable_number }} 已经使用平台{{
developer_used_info.all_use_number developer_used_info.all_use_number
}} 其他{{ developer_used_info.other_used_sum }} }} 其他{{ developer_used_info.other_used_sum }}
@ -208,11 +226,18 @@
<el-table-column <el-table-column
align="center" align="center"
fixed fixed
label="用户 issuer_id" label="开发者ID issuer_id"
prop="issuer_id" prop="issuer_id"
width="300"> width="200">
<template slot-scope="scope"> <template slot-scope="scope">
<el-popover placement="top" trigger="hover"> <el-popover placement="top" trigger="hover">
<el-tooltip content="点击复制到剪贴板">
<el-link v-clipboard:copy="scope.row.issuer_id"
v-clipboard:success="copy_success"
:underline="false">开发者账户ID: {{ scope.row.issuer_id }}
</el-link>
</el-tooltip>
<p>开发者账户已使用设备数: {{ scope.row.developer_used_number }}</p> <p>开发者账户已使用设备数: {{ scope.row.developer_used_number }}</p>
<p>开发者账户可用设备数: {{ 100 - scope.row.developer_used_number }}</p> <p>开发者账户可用设备数: {{ 100 - scope.row.developer_used_number }}</p>
<p>由于您设置可用设备数: {{ scope.row.usable_number }} ,所以现在可用设备数: {{ <p>由于您设置可用设备数: {{ scope.row.usable_number }} ,所以现在可用设备数: {{
@ -302,6 +327,46 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column
align="center"
label="应用签名"
prop="app_used_number"
width="60">
<template slot-scope="scope">
<el-popover placement="top" trigger="hover">
<p>签名了 {{ scope.row.app_used_number }} 个应用</p>
<div slot="reference" class="name-wrapper">
<el-link v-if="scope.row.app_used_number > 0" :underline="false"
@click="show_device_ubill(scope.row.issuer_id)">
<el-tag size="medium">{{ scope.row.app_used_number }}</el-tag>
</el-link>
<el-tag v-else size="medium">{{ scope.row.app_used_number }}</el-tag>
</div>
</el-popover>
</template>
</el-table-column>
<el-table-column
align="center"
label="专属账户"
prop="is_private"
width="60">
<template slot-scope="scope">
<el-popover placement="top"
trigger="hover">
<p>专属开发者被分配给了 {{ scope.row.app_private_number }} 个应用</p>
<p>专属开发者一共分配了 {{ scope.row.app_private_usable_number }} 个设备名额</p>
<p>专属开发者已经被专属应用使用了 {{ scope.row.app_private_used_number }} 个设备名额</p>
<div slot="reference" class="name-wrapper">
<el-link v-if="scope.row.app_private_number > 0" :underline="false"
@click="bindAppletoapp(scope.row)">
<el-tag size="medium"></el-tag>
</el-link>
<el-tag v-else size="medium" type="info" @click="bindAppletoapp(scope.row)"></el-tag>
</div>
</el-popover>
</template>
</el-table-column>
<el-table-column <el-table-column
:formatter="formatter" :formatter="formatter"
align="center" align="center"
@ -727,7 +792,7 @@
<el-input <el-input
v-model="appnamesearch" v-model="appnamesearch"
clearable clearable
placeholder="输入应用名称或BundleID" placeholder="输入应用名称或应用BundleID或开发者ID"
style="width: 30%;margin-right: 30px;margin-bottom: 10px"/> style="width: 30%;margin-right: 30px;margin-bottom: 10px"/>
<el-date-picker <el-date-picker
v-model="timerangesearch" v-model="timerangesearch"
@ -812,9 +877,11 @@ import {
iosudevices iosudevices
} from "@/restful"; } from "@/restful";
import {getUserInfoFun, removeAaary} from "@/utils"; import {getUserInfoFun, removeAaary} from "@/utils";
import AppleDeveloperBindApp from "@/components/base/AppleDeveloperBindApp";
export default { export default {
name: "FirSuperSignBase", name: "FirSuperSignBase",
components: {AppleDeveloperBindApp},
data() { data() {
return { return {
dialogShowDeviceBillInfo: false, dialogShowDeviceBillInfo: false,
@ -885,6 +952,18 @@ export default {
} }
}] }]
}, },
developer_options: [
{
value: 'private',
label: '专属应用账户'
}, {
value: 'public',
label: '公共应用账户'
}
],
developer_choice: '',
bind_appletoapp_sure: false,
appletoapp_title: ''
} }
}, watch: { }, watch: {
'dialogaddDeveloperVisible': function () { 'dialogaddDeveloperVisible': function () {
@ -894,6 +973,16 @@ export default {
} }
}, },
methods: { methods: {
copy_success() {
this.$message.success('复制剪切板成功');
},
bindAppletoapp(developinfo) {
if (developinfo) {
this.editdeveloperinfo = developinfo
}
this.appletoapp_title = "开发者ID " + this.editdeveloperinfo.issuer_id + " 【专属账户分配之后,其他应用将无法使用该专属开发者的设备余额】";
this.bind_appletoapp_sure = true;
},
disabledeveloperFun(developer, act) { disabledeveloperFun(developer, act) {
this.$confirm('如您不再使用该开发账户进行签名,可以点击进行禁用,是否继续?', '提示', { this.$confirm('如您不再使用该开发账户进行签名,可以点击进行禁用,是否继续?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
@ -1132,10 +1221,15 @@ export default {
this.activeName = 'useddevices' this.activeName = 'useddevices'
this.get_data_from_tabname(this.activeName); this.get_data_from_tabname(this.activeName);
}, },
show_device_ubill(issuer_id) {
this.appnamesearch = issuer_id;
this.activeName = 'devicesrank'
this.get_data_from_tabname(this.activeName);
},
get_data_from_tabname(tabname, data = {}) { get_data_from_tabname(tabname, data = {}) {
data.udid = this.udidsearch.replace(/^\s+|\s+$/g, ""); data.udid = this.udidsearch.replace(/^\s+|\s+$/g, "");
data.bundleid = this.Bundleidsearch.replace(/^\s+|\s+$/g, ""); data.bundleid = this.Bundleidsearch.replace(/^\s+|\s+$/g, "");
data.appid = this.appidseach.replace(/^\s+|\s+$/g, ""); data.issuer_id = this.appidseach.replace(/^\s+|\s+$/g, "");
this.$router.push({"name": 'FirSuperSignBase', params: {act: tabname}}); this.$router.push({"name": 'FirSuperSignBase', params: {act: tabname}});
if (tabname === "useddevices") { if (tabname === "useddevices") {
this.iosdevicesFun('GET', data) this.iosdevicesFun('GET', data)
@ -1146,6 +1240,7 @@ export default {
// this.title=''; // this.title='';
// this.dialogaddDeveloperVisible=true; // this.dialogaddDeveloperVisible=true;
} else if (tabname === "iosdeveloper") { } else if (tabname === "iosdeveloper") {
data.developer_choice = this.developer_choice;
this.iosdeveloperFun({"methods": "GET", "data": data}) this.iosdeveloperFun({"methods": "GET", "data": data})
} else if (tabname === "devicesbill") { } else if (tabname === "devicesbill") {
this.iosdevicebillFun({"methods": "GET", "data": data}) this.iosdevicebillFun({"methods": "GET", "data": data})

@ -67,6 +67,7 @@ import {
Timeline, Timeline,
TimelineItem, TimelineItem,
Tooltip, Tooltip,
Transfer,
Upload Upload
} from "element-ui"; } from "element-ui";
import VueLazyload from 'vue-lazyload' import VueLazyload from 'vue-lazyload'
@ -156,6 +157,7 @@ Vue.use(Footer);
Vue.use(InputNumber); Vue.use(InputNumber);
Vue.use(Radio); Vue.use(Radio);
Vue.use(Backtop); Vue.use(Backtop);
Vue.use(Transfer);
Vue.prototype.$message = Message; Vue.prototype.$message = Message;
Vue.prototype.$notify = Notification; Vue.prototype.$notify = Notification;
Vue.prototype.$loading = Loading.service; Vue.prototype.$loading = Loading.service;

@ -768,6 +768,21 @@ export function DeviceRankInfo(callBack, params, load = true) {
); );
} }
/**开发绑定应用*/
export function developerBindAppFun(callBack, params, load = true) {
getData(
params.methods,
USERSEVER + '/supersign/bind',
params.data,
data => {
callBack(data);
},
load,
true,
true
);
}
/**应用举报 */ /**应用举报 */
export function appReport(callBack, params, load = true) { export function appReport(callBack, params, load = true) {
getData( getData(

@ -82,7 +82,7 @@ if (page) {
} }
} }
const version='1.3.4'; const version='1.3.6';
const pro_base_env = { const pro_base_env = {
baseUrl: 'https://flyapps.cn', baseUrl: 'https://flyapps.cn',

@ -0,0 +1,30 @@
# Generated by Django 3.2.3 on 2021-12-13 16:43
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0025_delete_devicequeue'),
]
operations = [
migrations.CreateModel(
name='AppleDeveloperToAppUse',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('usable_number', models.IntegerField(default=100, verbose_name='可使用设备数')),
('description', models.CharField(blank=True, default='', max_length=256, verbose_name='备注')),
('created_time', models.DateTimeField(auto_now_add=True, verbose_name='添加时间')),
('app_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.apps')),
('developerid',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.appiosdeveloperinfo')),
],
options={
'verbose_name': '开发者专属于应用',
'verbose_name_plural': '开发者专属于应用',
'unique_together': {('app_id', 'developerid')},
},
),
]

@ -599,3 +599,19 @@ class AppReportInfo(models.Model):
def __str__(self): def __str__(self):
return "%s-%s" % (self.app_name, self.report_reason) return "%s-%s" % (self.app_name, self.report_reason)
class AppleDeveloperToAppUse(models.Model):
app_id = models.ForeignKey(to='Apps', on_delete=models.CASCADE)
developerid = models.ForeignKey(to='AppIOSDeveloperInfo', on_delete=models.CASCADE)
usable_number = models.IntegerField(verbose_name="可使用设备数", default=100)
description = models.CharField(verbose_name="备注", max_length=256, default='', blank=True)
created_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间")
class Meta:
verbose_name = '开发者专属于应用'
verbose_name_plural = "开发者专属于应用"
unique_together = ('app_id', 'developerid')
def __str__(self):
return "%s-%s" % (self.app_id.name, self.developerid.issuer_id)

@ -27,7 +27,7 @@ from api.views.receiveudids import IosUDIDView, TaskView
from api.views.report import ReportView from api.views.report import ReportView
from api.views.storage import StorageView, CleanStorageView from api.views.storage import StorageView, CleanStorageView
from api.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView, SuperSignCertView, \ from api.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView, SuperSignCertView, \
DeviceUsedBillView, DeveloperDeviceView, DeviceUsedRankInfoView DeviceUsedBillView, DeveloperDeviceView, DeviceUsedRankInfoView, AppleDeveloperBindAppsView
from api.views.thirdlogin import ValidWxChatToken, ThirdWxAccount from api.views.thirdlogin import ValidWxChatToken, ThirdWxAccount
from api.views.uploads import AppAnalyseView, UploadView from api.views.uploads import AppAnalyseView, UploadView
@ -64,6 +64,7 @@ urlpatterns = [
re_path("^supersign/cert$", SuperSignCertView.as_view()), re_path("^supersign/cert$", SuperSignCertView.as_view()),
re_path("^supersign/bill$", DeviceUsedBillView.as_view()), re_path("^supersign/bill$", DeviceUsedBillView.as_view()),
re_path("^supersign/rank$", DeviceUsedRankInfoView.as_view()), re_path("^supersign/rank$", DeviceUsedRankInfoView.as_view()),
re_path("^supersign/bind$", AppleDeveloperBindAppsView.as_view()),
re_path("^package_prices$", PriceView.as_view()), re_path("^package_prices$", PriceView.as_view()),
re_path("^orders$", OrderView.as_view()), re_path("^orders$", OrderView.as_view()),
re_path("^certification$", CertificationView.as_view()), re_path("^certification$", CertificationView.as_view()),

@ -16,7 +16,7 @@ from django.core.cache import cache
from django.db.models import Count from django.db.models import Count
from api.models import APPSuperSignUsedInfo, AppUDID, AppIOSDeveloperInfo, AppReleaseInfo, Apps, APPToDeveloper, \ from api.models import APPSuperSignUsedInfo, AppUDID, AppIOSDeveloperInfo, AppReleaseInfo, Apps, APPToDeveloper, \
UDIDsyncDeveloper, DeveloperAppID, DeveloperDevicesID, IosDeveloperPublicPoolBill, UserInfo UDIDsyncDeveloper, DeveloperAppID, DeveloperDevicesID, IosDeveloperPublicPoolBill, UserInfo, AppleDeveloperToAppUse
from api.utils.app.iossignapi import ResignApp, AppDeveloperApiV2 from api.utils.app.iossignapi import ResignApp, AppDeveloperApiV2
from api.utils.baseutils import file_format_path, delete_app_profile_file, get_profile_full_path, format_apple_date, \ from api.utils.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 get_format_time, make_app_uuid, make_from_user_uuid
@ -270,6 +270,31 @@ def run_function_by_lock(lock_key='', timeout=60 * 5):
return decorator return decorator
def get_new_developer_by_app_obj(user_objs, app_obj, apple_to_app=False):
can_used_developer_pk_list = []
obj_base_filter = AppIOSDeveloperInfo.objects.filter(user_id__in=user_objs, is_actived=True, certid__isnull=False)
if apple_to_app:
# 存在指定开发者,则新设备优先注册到指定开发者
developer_obj_lists = obj_base_filter.filter(appledevelopertoappuse__app_id=app_obj)
else:
developer_obj_lists = obj_base_filter.exclude(appledevelopertoappuse__developerid__isnull=False)
developer_obj_lists = developer_obj_lists.distinct().order_by("created_time")
for developer_obj in developer_obj_lists:
# 通过开发者数限制进行过滤
used_number = get_developer_udided(developer_obj)[2] + get_developer_can_used_from_public_sign(
developer_obj.user_id)
if used_number < developer_obj.usable_number:
if apple_to_app:
apple_to_app_obj = AppleDeveloperToAppUse.objects.filter(app_id=app_obj,
developerid=developer_obj).first()
if apple_to_app_obj:
if used_number < apple_to_app_obj.usable_number:
can_used_developer_pk_list.append(developer_obj.pk)
else:
can_used_developer_pk_list.append(developer_obj.pk)
return can_used_developer_pk_list
def get_developer_user_by_app_udid(user_objs, udid, app_obj): def get_developer_user_by_app_udid(user_objs, udid, app_obj):
developer_udid_obj_list = UDIDsyncDeveloper.objects.filter(developerid__user_id__in=user_objs, developer_udid_obj_list = UDIDsyncDeveloper.objects.filter(developerid__user_id__in=user_objs,
developerid__is_actived=True, developerid__is_actived=True,
@ -296,12 +321,11 @@ def get_developer_user_by_app_udid(user_objs, udid, app_obj):
developer_obj.user_id) < developer_obj.usable_number: developer_obj.user_id) < developer_obj.usable_number:
logger.info(f'app_obj:{app_obj} only and return') logger.info(f'app_obj:{app_obj} only and return')
return developer_obj, False return developer_obj, False
can_used_developer_pk_list = []
for developer_obj in AppIOSDeveloperInfo.objects.filter(user_id__in=user_objs, is_actived=True, apple_to_app = AppleDeveloperToAppUse.objects.filter(app_id=app_obj).first()
certid__isnull=False).order_by("created_time"): can_used_developer_pk_list = get_new_developer_by_app_obj(user_objs, app_obj, apple_to_app)
if get_developer_udided(developer_obj)[2] + get_developer_can_used_from_public_sign( if not can_used_developer_pk_list:
developer_obj.user_id) < developer_obj.usable_number: can_used_developer_pk_list = get_new_developer_by_app_obj(user_objs, app_obj)
can_used_developer_pk_list.append(developer_obj.pk)
# 查询开发者策略 按照最小注册设备数进行查找,这样可以分散,进而使应用趋向于一个开发者,为后期更新提供方便 # 查询开发者策略 按照最小注册设备数进行查找,这样可以分散,进而使应用趋向于一个开发者,为后期更新提供方便
if can_used_developer_pk_list: if can_used_developer_pk_list:

@ -1,6 +1,7 @@
import logging import logging
import os import os
from django.db.models import Sum
from rest_framework import serializers from rest_framework import serializers
from api import models from api import models
@ -153,6 +154,11 @@ class AppsSerializer(serializers.ModelSerializer):
def get_preview_url(self, obj): def get_preview_url(self, obj):
return get_redirect_server_domain(None, obj.user_id, get_app_domain_name(obj)) return get_redirect_server_domain(None, obj.user_id, get_app_domain_name(obj))
private_developer_number = serializers.SerializerMethodField()
def get_private_developer_number(self, obj):
return models.AppleDeveloperToAppUse.objects.filter(app_id=obj).count()
domain_name = serializers.SerializerMethodField() domain_name = serializers.SerializerMethodField()
def get_domain_name(self, obj): def get_domain_name(self, obj):
@ -450,6 +456,21 @@ class DeveloperSerializer(serializers.ModelSerializer):
developer_used_number = serializers.SerializerMethodField() developer_used_number = serializers.SerializerMethodField()
developer_used_other_number = serializers.SerializerMethodField() developer_used_other_number = serializers.SerializerMethodField()
use_number = serializers.SerializerMethodField() use_number = serializers.SerializerMethodField()
app_used_number = serializers.SerializerMethodField()
is_disabled = serializers.SerializerMethodField()
app_private_number = serializers.SerializerMethodField()
app_private_used_number = serializers.SerializerMethodField()
app_usable_number = serializers.SerializerMethodField()
app_private_usable_number = serializers.SerializerMethodField()
def get_app_usable_number(self, obj):
app_id = self.context.get('app_id')
if app_id:
apple_to_app_obj = models.AppleDeveloperToAppUse.objects.filter(developerid=obj,
app_id__app_id=app_id).first()
if apple_to_app_obj:
return apple_to_app_obj.usable_number
return 0
def get_developer_used_number(self, obj): def get_developer_used_number(self, obj):
return models.UDIDsyncDeveloper.objects.filter(developerid=obj).count() return models.UDIDsyncDeveloper.objects.filter(developerid=obj).count()
@ -457,9 +478,28 @@ class DeveloperSerializer(serializers.ModelSerializer):
def get_developer_used_other_number(self, obj): def get_developer_used_other_number(self, obj):
return get_developer_udided(obj)[0] return get_developer_udided(obj)[0]
def get_is_disabled(self, obj):
return bool(1 - obj.is_actived)
def get_app_private_number(self, obj):
return models.AppleDeveloperToAppUse.objects.filter(developerid=obj).count()
def get_app_private_usable_number(self, obj):
used_number = models.AppleDeveloperToAppUse.objects.filter(developerid=obj).values('usable_number').aggregate(
Sum('usable_number')).get('usable_number__sum')
if not used_number:
used_number = 0
return used_number
def get_app_private_used_number(self, obj):
return models.DeveloperDevicesID.objects.filter(developerid=obj, app_id__appledevelopertoappuse__developerid=obj).values('udid').distinct().count()
def get_use_number(self, obj): def get_use_number(self, obj):
return models.DeveloperDevicesID.objects.filter(developerid=obj).values('udid').distinct().count() return models.DeveloperDevicesID.objects.filter(developerid=obj).values('udid').distinct().count()
def get_app_used_number(self, obj):
return models.DeveloperAppID.objects.filter(developerid=obj).count()
class AdminDeveloperSerializer(DeveloperSerializer): class AdminDeveloperSerializer(DeveloperSerializer):
class Meta: class Meta:
@ -786,3 +826,37 @@ class AdminAppReportSerializer(AppReportSerializer):
def get_status_choices(self, obj): def get_status_choices(self, obj):
return get_choices_dict(obj.status_choices) return get_choices_dict(obj.status_choices)
class AppleDeveloperToAppUseSerializer(serializers.ModelSerializer):
class Meta:
model = models.AppleDeveloperToAppUse
# exclude = ["user_id", "to_user_id", "developer_info", "app_info", "udid_sync_info", "app_id"]
fields = ["issuer_id", "app_id", "created_time", "description", "usable_number"]
issuer_id = serializers.CharField(source="developerid.issuer_id")
app_id = serializers.CharField(source="app_id.app_id")
class AppleDeveloperToAppUseAppsSerializer(serializers.ModelSerializer):
class Meta:
model = models.Apps
fields = ["app_id", "name", "short", "bundle_id", "description", "app_usable_number", "app_used_number"]
app_usable_number = serializers.SerializerMethodField()
app_used_number = serializers.SerializerMethodField()
def get_app_usable_number(self, obj):
issuer_id = self.context.get('issuer_id')
if issuer_id:
apple_to_app_obj = models.AppleDeveloperToAppUse.objects.filter(developerid__issuer_id=issuer_id,
app_id=obj).first()
if apple_to_app_obj:
return apple_to_app_obj.usable_number
return 0
def get_app_used_number(self, obj):
issuer_id = self.context.get('issuer_id')
if issuer_id:
return models.DeveloperDevicesID.objects.filter(app_id=obj,developerid__issuer_id=issuer_id).count()
return 0

@ -13,13 +13,13 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from api.models import AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppUDID, IosDeveloperPublicPoolBill, \ from api.models import AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppUDID, IosDeveloperPublicPoolBill, \
UDIDsyncDeveloper UDIDsyncDeveloper, AppleDeveloperToAppUse, Apps
from api.utils.app.supersignutils import IosUtils from api.utils.app.supersignutils import IosUtils
from api.utils.auth import ExpiringTokenAuthentication, SuperSignPermission from api.utils.auth import ExpiringTokenAuthentication, SuperSignPermission
from api.utils.modelutils import get_user_public_used_sign_num, get_user_public_sign_num from api.utils.modelutils import get_user_public_used_sign_num, get_user_public_sign_num
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.serializer import DeveloperSerializer, SuperSignUsedSerializer, DeviceUDIDSerializer, BillInfoSerializer, \ from api.utils.serializer import DeveloperSerializer, SuperSignUsedSerializer, DeviceUDIDSerializer, BillInfoSerializer, \
DeveloperDeviceSerializer DeveloperDeviceSerializer, AppleDeveloperToAppUseSerializer, AppleDeveloperToAppUseAppsSerializer
from api.utils.storage.caches import CleanSignDataState from api.utils.storage.caches import CleanSignDataState
from api.utils.utils import get_developer_devices, get_choices_dict from api.utils.utils import get_developer_devices, get_choices_dict
@ -41,17 +41,26 @@ class DeveloperView(APIView):
res = BaseResponse() res = BaseResponse()
app_id = request.query_params.get("appid", None) issuer_id = request.query_params.get("issuer_id", '')
app_id = request.query_params.get("app_id", '')
developer_choice = request.query_params.get("developer_choice", None)
developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=request.user) developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=request.user)
if developer_choice and developer_choice in ['private', 'public']:
if developer_choice == 'public':
developer_obj = developer_obj.exclude(appledevelopertoappuse__developerid__isnull=False)
else:
developer_obj = developer_obj.filter(appledevelopertoappuse__developerid__isnull=False)
developer_obj = developer_obj.distinct()
res.use_num = get_developer_devices(developer_obj) res.use_num = get_developer_devices(developer_obj)
if app_id: if issuer_id:
developer_obj = developer_obj.filter(issuer_id=app_id) developer_obj = developer_obj.filter(issuer_id=issuer_id)
page_obj = PageNumber() page_obj = PageNumber()
app_page_serializer = page_obj.paginate_queryset(queryset=developer_obj.order_by("-updated_time"), app_page_serializer = page_obj.paginate_queryset(queryset=developer_obj.order_by("-updated_time"),
request=request, request=request,
view=self) view=self)
developer_serializer = DeveloperSerializer(app_page_serializer, many=True, ) developer_serializer = DeveloperSerializer(app_page_serializer, many=True, context={'app_id': app_id})
res.data = developer_serializer.data res.data = developer_serializer.data
res.count = developer_obj.count() res.count = developer_obj.count()
@ -223,14 +232,14 @@ class DeveloperView(APIView):
def base_super_sign_used_info(request): def base_super_sign_used_info(request):
udid = request.query_params.get("udid", None) udid = request.query_params.get("udid", None)
bundle_id = request.query_params.get("bundleid", None) bundle_id = request.query_params.get("bundleid", None)
developer_id = request.query_params.get("appid", None) issuer_id = request.query_params.get("issuer_id", None)
mine = True mine = True
super_sign_used_objs = APPSuperSignUsedInfo.objects.filter(developerid__user_id=request.user, ) super_sign_used_objs = APPSuperSignUsedInfo.objects.filter(developerid__user_id=request.user, )
if not super_sign_used_objs: if not super_sign_used_objs:
super_sign_used_objs = APPSuperSignUsedInfo.objects.filter(user_id=request.user, ) super_sign_used_objs = APPSuperSignUsedInfo.objects.filter(user_id=request.user, )
mine = False mine = False
if developer_id: if issuer_id:
super_sign_used_objs = super_sign_used_objs.filter(developerid__issuer_id=developer_id) super_sign_used_objs = super_sign_used_objs.filter(developerid__issuer_id=issuer_id)
if udid: if udid:
super_sign_used_objs = super_sign_used_objs.filter(udid__udid__udid=udid) super_sign_used_objs = super_sign_used_objs.filter(udid__udid__udid=udid)
if bundle_id: if bundle_id:
@ -299,10 +308,10 @@ class DeveloperDeviceView(APIView):
res = BaseResponse() res = BaseResponse()
udid = request.query_params.get("udid", None) udid = request.query_params.get("udid", None)
developer_id = request.query_params.get("appid", None) issuer_id = request.query_params.get("issuer_id", None)
super_sign_used_objs = UDIDsyncDeveloper.objects.filter(developerid__user_id=request.user, ) super_sign_used_objs = UDIDsyncDeveloper.objects.filter(developerid__user_id=request.user, )
if developer_id: if issuer_id:
super_sign_used_objs = super_sign_used_objs.filter(developerid__issuer_id=developer_id) super_sign_used_objs = super_sign_used_objs.filter(developerid__issuer_id=issuer_id)
if udid: if udid:
super_sign_used_objs = super_sign_used_objs.filter(udid=udid) super_sign_used_objs = super_sign_used_objs.filter(udid=udid)
page_obj = PageNumber() page_obj = PageNumber()
@ -318,7 +327,7 @@ class DeveloperDeviceView(APIView):
res = BaseResponse() res = BaseResponse()
pk = request.query_params.get("id", None) pk = request.query_params.get("id", None)
app_id = request.query_params.get("aid", None) app_id = request.query_params.get("aid", None)
app_udid_obj = AppUDID.objects.filter(pk=pk) app_udid_obj = AppUDID.objects.filter(pk=pk, app_id__user_id=request.user)
if app_udid_obj: if app_udid_obj:
super_sign_used_obj = APPSuperSignUsedInfo.objects.filter(udid=app_udid_obj.first()).first() super_sign_used_obj = APPSuperSignUsedInfo.objects.filter(udid=app_udid_obj.first()).first()
if super_sign_used_obj and super_sign_used_obj.developerid.user_id.pk == request.user.pk: if super_sign_used_obj and super_sign_used_obj.developerid.user_id.pk == request.user.pk:
@ -430,7 +439,8 @@ class DeviceUsedRankInfoView(APIView):
app_used_sign_objs = APPSuperSignUsedInfo.objects.filter(user_id=request.user) app_used_sign_objs = APPSuperSignUsedInfo.objects.filter(user_id=request.user)
if appnamesearch: if appnamesearch:
app_used_sign_objs = app_used_sign_objs.filter( app_used_sign_objs = app_used_sign_objs.filter(
Q(app_id__name=appnamesearch) | Q(app_id__bundle_id=appnamesearch)) Q(app_id__name=appnamesearch) | Q(app_id__bundle_id=appnamesearch) | Q(
developerid__issuer_id=appnamesearch))
if end_time and start_time: if end_time and start_time:
try: try:
start_time = datetime.date.fromtimestamp(int(start_time) / 1000) start_time = datetime.date.fromtimestamp(int(start_time) / 1000)
@ -455,3 +465,93 @@ class DeviceUsedRankInfoView(APIView):
new_app_used_sign_infos.append(new_app_used_info) new_app_used_sign_infos.append(new_app_used_info)
res.data = new_app_used_sign_infos res.data = new_app_used_sign_infos
return Response(res.dict) return Response(res.dict)
class AppleDeveloperBindAppsView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
permission_classes = [SuperSignPermission, ]
def get(self, request):
res = BaseResponse()
act = request.query_params.get("act", '')
issuer_id = request.query_params.get("issuer_id", '')
app_id = request.query_params.get("app_id", '')
if act == 'apps':
apps_obj_list = Apps.objects.filter(user_id=request.user, type=1).all()
app_serializer = AppleDeveloperToAppUseAppsSerializer(apps_obj_list, many=True,
context={'issuer_id': issuer_id})
else:
apple_to_app_obj = AppleDeveloperToAppUse.objects.filter(app_id__user_id=request.user)
if issuer_id:
apple_to_app_obj = apple_to_app_obj.filter(developerid__issuer_id=issuer_id)
if app_id:
apple_to_app_obj = apple_to_app_obj.filter(app_id__app_id=app_id)
app_serializer = AppleDeveloperToAppUseSerializer(apple_to_app_obj.all(), many=True)
res.data = app_serializer.data
return Response(res.dict)
def post(self, request):
res = BaseResponse()
issuer_id = request.data.get('issuer_id', '')
app_id = request.data.get('app_id', '')
choices_data = request.data.get('choices_data', [])
logger.info(f"change apple to app data:{request.data}")
many_obj = []
if app_id:
app_obj = Apps.objects.filter(app_id=app_id, type=1, user_id=request.user).first()
issuer_id_list = [apple_to_app['developerid__issuer_id'] for apple_to_app in
AppleDeveloperToAppUse.objects.filter(app_id=app_obj).values(
'developerid__issuer_id').all().distinct()]
add_issuer_ids = list(set(choices_data) - set(issuer_id_list))
remove_issuer_ids = list(set(issuer_id_list) - set(choices_data))
for item in add_issuer_ids:
developer_obj = AppIOSDeveloperInfo.objects.filter(issuer_id=item, user_id=request.user).first()
if developer_obj:
many_obj.append(AppleDeveloperToAppUse(developerid=developer_obj, app_id=app_obj))
AppleDeveloperToAppUse.objects.filter(developerid__issuer_id__in=remove_issuer_ids, app_id=app_obj).delete()
if issuer_id:
developer_obj = AppIOSDeveloperInfo.objects.filter(issuer_id=issuer_id, user_id=request.user).first()
app_id_list = [apple_to_app['app_id__app_id'] for apple_to_app in
AppleDeveloperToAppUse.objects.filter(developerid=developer_obj).values(
'app_id__app_id').all().distinct()]
add_app_ids = list(set(choices_data) - set(app_id_list))
remove_app_ids = list(set(app_id_list) - set(choices_data))
for item in add_app_ids:
app_obj = Apps.objects.filter(app_id=item, type=1, user_id=request.user).first()
if app_obj:
many_obj.append(AppleDeveloperToAppUse(developerid=developer_obj, app_id=app_obj))
AppleDeveloperToAppUse.objects.filter(developerid=developer_obj, app_id__app_id__in=remove_app_ids,
app_id__user_id=request.user).delete()
if many_obj:
AppleDeveloperToAppUse.objects.bulk_create(many_obj)
return Response(res.dict)
def put(self, request):
res = BaseResponse()
issuer_id = request.data.get('issuer_id', '')
app_id = request.data.get('app_id', '')
infos = request.data.get('infos', {})
logger.info(f"change apple to app data:{request.data}")
if issuer_id and not app_id:
app_id = infos.get('app_id', '')
if app_id and not issuer_id:
issuer_id = infos.get('issuer_id', '')
if app_id and issuer_id:
try:
app_usable_number = int(infos.get('app_usable_number', 100))
if 0 < app_usable_number < 101:
AppleDeveloperToAppUse.objects.filter(developerid__issuer_id=issuer_id,
app_id__app_id=app_id, app_id__user_id=request.user).update(
usable_number=int(app_usable_number))
except Exception as e:
logger.error(f'update app developer used failed infos:{infos} Exception:{e}')
return Response(res.dict)

Loading…
Cancel
Save