管理后台增加超级签充值功能,前端页面增加udid展示

qrnn
youngS 3 years ago
parent c170086609
commit d550d20998
  1. 31
      fir_admin/src/api/developer.js
  2. 21
      fir_admin/src/router/index.js
  3. 117
      fir_admin/src/views/supersign/bill/add.vue
  4. 203
      fir_admin/src/views/supersign/bill/list.vue
  5. 178
      fir_client/src/components/user/FirSuperSignBase.vue
  6. 16
      fir_client/src/restful/index.js
  7. 4
      fir_ser/admin/urls.py
  8. 95
      fir_ser/admin/views/supersign.py
  9. 4
      fir_ser/api/urls.py
  10. 35
      fir_ser/api/utils/modelutils.py
  11. 30
      fir_ser/api/utils/serializer.py
  12. 13
      fir_ser/api/utils/utils.py
  13. 88
      fir_ser/api/views/supersign.py

@ -15,3 +15,34 @@ export function updatedeveloperInfo(data) {
data data
}) })
} }
export function getBillInfo(query) {
return request({
url: '/bill/info',
method: 'get',
params: query
})
}
export function addBillInfo(data) {
return request({
url: '/bill/info',
method: 'post',
data
})
}
export function delBillInfo(data) {
return request({
url: '/bill/info',
method: 'delete',
data
})
}
export function getUserBillInfo(query) {
return request({
url: '/bill/userinfo',
method: 'get',
params: query
})
}

@ -278,6 +278,27 @@ export const constantRoutes = [
meta: { title: '设备消耗', icon: 'form' } meta: { title: '设备消耗', icon: 'form' }
} }
] ]
},
{
path: 'bill',
name: 'bill',
redirect: '/supersign/bill/list',
component: () => import('@/views/supersign/index'),
meta: { title: '设备账单', icon: 'form' },
children: [
{
path: 'list',
name: 'bill_info_list',
component: () => import('@/views/supersign/bill/list'),
meta: { title: '消耗账单', icon: 'form' }
},
{
path: 'add',
name: 'bill_info_add',
component: () => import('@/views/supersign/bill/add'),
meta: { title: '设备充值', icon: 'form' }
}
]
} }
] ]
}, },

@ -0,0 +1,117 @@
<template>
<div class="app-container">
<el-form ref="postForm" :model="postForm" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="用户ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.user_id" placeholder="邮箱,手机号,或者uid" />
</el-col>
<el-col :span="4">
<el-button @click="check_balance('user_id')">查询校验</el-button>
</el-col>
</el-row>
<el-row v-if="user_info.private_balance_info.all_balance+user_info.public_balance_info.all_balance">
<el-col>
<el-link :underline="false"> 公有池数量 {{ user_info.public_balance_info.all_balance }} 已经使用数量 {{ user_info.public_balance_info.used_balance }}</el-link>
</el-col>
<el-col>
<el-link :underline="false"> 私有池数量 {{ user_info.private_balance_info.all_balance }} 已经使用数量 {{ user_info.private_balance_info.used_balance }}</el-link>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="目标用户">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.to_user_id" placeholder="邮箱,手机号,或者uid" />
</el-col>
<el-col :span="4">
<el-button @click="check_balance('to_user_id')">查询校验</el-button>
</el-col>
</el-row>
<el-row v-if="to_user_info.private_balance_info.all_balance+to_user_info.public_balance_info.all_balance">
<el-col>
<el-link :underline="false"> 公有池数量 {{ to_user_info.public_balance_info.all_balance }} 已经使用数量 {{ to_user_info.public_balance_info.used_balance }}</el-link>
</el-col>
<el-col>
<el-link :underline="false"> 私有池数量 {{ to_user_info.private_balance_info.all_balance }} 已经使用数量 {{ to_user_info.private_balance_info.used_balance }}</el-link>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="转移数量">
<el-row :gutter="12">
<el-col :span="16">
<el-input-number v-model="postForm.number" :min="0" :max="10000" label="可使用设备数" />
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-button type="primary" @click="addbill">确定</el-button>
</el-col>
</div>
</template>
<script>
import waves from '@/directive/waves'
import { addBillInfo, getUserBillInfo } from '@/api/developer'
const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }
]
export default {
name: 'BillAddInfo',
directives: { waves },
filters: {
formatTime(time) {
return time.split('T')[0]
}
},
data() {
return {
list: null,
listLoading: true,
total: 0,
postForm: {
to_user_id: undefined,
user_id: undefined,
number: undefined
},
user_info: { public_balance_info: { all_balance: undefined }, private_balance_info: { used_balance: undefined }},
to_user_info: { public_balance_info: { all_balance: undefined }, private_balance_info: { used_balance: undefined }},
action_choices: undefined,
sortOptions
}
},
created() {
},
methods: {
check_balance(act) {
let user_id = this.postForm.user_id
if (act === 'to_user_id') {
user_id = this.postForm.to_user_id
}
getUserBillInfo({ user_id: user_id }).then(response => {
if (act === 'user_id') {
this.user_info = response
} else if (act === 'to_user_id') {
this.to_user_info = response
}
console.log(this.user_info)
})
},
addbill() {
addBillInfo(this.postForm).then(response => {
this.$message.success('充值成功')
})
}
}
}
</script>

@ -0,0 +1,203 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="listQuery.user_id" placeholder="用户ID" style="width: 140px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.to_user_id" placeholder="目标用户" style="width: 300px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.udid" placeholder="设备UDID" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.app_id" placeholder="应用id" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.action" placeholder="充值类型" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in action_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
Search
</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="Loading"
border
fit
highlight-current-row
stripe
>
<el-table-column align="center" label="ID" width="90">
<template slot-scope="scope">
{{ scope.row.id }}
</template>
</el-table-column>
<el-table-column label="用户ID" width="100" align="center">
<template slot-scope="scope">
<router-link :to="{name: 'user_info_edit',params:{id:scope.row.user_id}}">
<el-link type="primary"> {{ scope.row.user_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="目标用户ID" width="100" align="center">
<template slot-scope="scope">
<router-link v-if="scope.row.to_user_id" :to="{name: 'user_info_edit',params:{id:scope.row.to_user_id}}">
<el-link type="primary"> {{ scope.row.to_user_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="应用ID" width="100" align="center">
<template slot-scope="scope">
<router-link v-if="scope.row.app_id" :to="{name: 'app_info_edit',params:{id:scope.row.app_id}}">
<el-link type="primary"> {{ scope.row.app_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="UDID" align="center">
<template slot-scope="scope">
{{ scope.row.udid }}
</template>
</el-table-column>
<el-table-column label="客户端IP" align="center" >
<template slot-scope="scope">
{{ scope.row.remote_addr }}
</template>
</el-table-column>
<el-table-column label="账单类型" align="center" width="100">
<template slot-scope="scope">
{{ scope.row.action }}
</template>
</el-table-column>
<el-table-column label="备注" align="center">
<template slot-scope="scope">
{{ scope.row.description }}
</template>
</el-table-column>
<el-table-column align="center" prop="created_time" label="创建时间" width="120">
<template slot-scope="scope">
<i class="el-icon-time" />
<el-tooltip :content="scope.row.created_time">
<span>{{ scope.row.created_time|formatTime }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="danger" size="mini" @click="delbill(scope.row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="fetchData" />
</div>
</template>
<script>
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves'
import { getBillInfo, delBillInfo } from '@/api/developer'
const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }
]
export default {
name: 'BillInfo',
components: { Pagination },
directives: { waves },
filters: {
formatTime(time) {
return time.split('T')[0]
},
certStatusFilter(status) {
const statusMap = {
'0': 'danger',
'1': 'success',
'2': 'gray',
'3': 'info'
}
return statusMap[status]
},
certLableFilter(row) {
for (const v of row.auth_type_choices) {
if (v.id === row.auth_type) {
return v.name
}
}
},
statusFilter(status) {
const statusMap = {
true: 'success',
false: 'danger'
}
return statusMap[status]
},
appStatusNameFilter(row) {
for (const r of row.status_choices) {
if (r.id === row.status) {
return r.name
}
}
},
appStatusFilter(status) {
const statusMap = {
'0': 'danger',
'1': 'success',
'2': 'gray'
}
return statusMap[status]
}
},
data() {
return {
list: null,
listLoading: true,
total: 0,
listQuery: {
page: 1,
limit: 10,
sort: '-created_time',
action: undefined,
app_id: undefined,
to_user_id: undefined,
user_id: undefined,
udid: undefined
},
action_choices: undefined,
sortOptions
}
},
created() {
this.listQuery.issuer_id = this.$route.params && this.$route.params.issuer_id
this.listQuery.user_id = this.$route.params && this.$route.params.user_id
this.fetchData()
},
methods: {
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
delbill(app_id) {
delBillInfo({ id: app_id }).then(response => {
this.$message.success('删除成功')
this.fetchData()
this.listLoading = false
})
},
fetchData() {
console.log(this.listQuery)
this.listLoading = true
getBillInfo(this.listQuery).then(response => {
this.list = response.data
if (this.list && this.list.length > 0) {
this.action_choices = this.list[0].action_choices
}
this.total = response.total
this.listLoading = false
})
}
}
}
</script>

@ -132,23 +132,27 @@
<el-table-column <el-table-column
align="center" align="center"
label="应用状态" label="应用状态"
width="110"> >
<template slot-scope="scope"> <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-if="scope.row.app_status===false" type="info">应用删除</el-tag>
<el-tag v-else> <el-tag v-else>
应用存在 应用存在
</el-tag> </el-tag>
</div>
<span v-else>{{ scope.row.description }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
align="center" align="center"
label="设备状态" label="设备状态">
width="110">
<template slot-scope="scope"> <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-if="scope.row.is_used===false" type="info">已经释放</el-tag>
<el-tag v-else> <el-tag v-else>
使用中 使用中
</el-tag> </el-tag>
</div>
</template> </template>
</el-table-column> </el-table-column>
@ -174,15 +178,15 @@
<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: 30%;margin-right: 30px;margin-bottom: 10px"/>
<el-button icon="el-icon-search" type="primary" @click="handleCurrentChange(pagination.currentPage)"> <el-button icon="el-icon-search" type="primary" @click="handleCurrentChange(pagination.currentPage)">
搜索 搜索
</el-button> </el-button>
<div style="width: 40%;margin-right: 30px;float:right"> <div style="width: 43%;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 }}
还剩{{ developer_used_info.all_usable_number - developer_used_info.flyapp_used_sum }} 可用 还剩{{ developer_used_info.all_usable_number - developer_used_info.flyapp_used_sum }} 可用
</el-link> </el-link>
<el-progress <el-progress
@ -230,7 +234,9 @@
prop="is_actived" prop="is_actived"
width="110"> width="110">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button v-if="scope.row.is_actived === true" size="small" type="success">已激活</el-button> <el-tooltip v-if="scope.row.is_actived === true" content="点击禁用">
<el-button size="small" type="success" @click="disabledeveloperFun(scope.row,'disable')">已激活</el-button>
</el-tooltip>
<el-button v-else size="small" type="danger" <el-button v-else size="small" type="danger"
@click="activedeveloperFun(scope.row,'checkauth')">点击激活 @click="activedeveloperFun(scope.row,'checkauth')">点击激活
</el-button> </el-button>
@ -383,6 +389,71 @@
<p>3.添加后激活开发者账户然后您就可以上传p12证书或者通过系统自动创建证书设备和描述文件请勿删除这些文件否则会导致用户安装的软件闪退或无法安装</p> <p>3.添加后激活开发者账户然后您就可以上传p12证书或者通过系统自动创建证书设备和描述文件请勿删除这些文件否则会导致用户安装的软件闪退或无法安装</p>
</el-card> </el-card>
</el-tab-pane>
<el-tab-pane label="UDID管理" name="iosudevices">
<el-input
v-model="udidsearch"
clearable
placeholder="输入UDID"
style="width: 30%;margin-right: 30px;margin-bottom: 10px"/>
<el-input
v-model="appidseach"
clearable
placeholder="输入开发者用户ID"
style="width: 30%;margin-right: 30px;margin-bottom: 10px"/>
<el-button icon="el-icon-search" type="primary" @click="handleCurrentChange(pagination.currentPage)">
搜索
</el-button>
<el-button @click="syncalldevices">
同步设备信息
</el-button>
<el-table
v-loading="loading"
:data="developer_udevices_lists"
border
stripe
style="width: 100%">
<el-table-column
align="center"
fixed
label="设备序列号"
prop="serial"
width="120"
>
</el-table-column>
<el-table-column
align="center"
label="设备名称"
prop="product"
width="120">
</el-table-column>
<el-table-column
align="center"
label="设备UDID"
prop="udid">
</el-table-column>
<el-table-column
align="center"
label="设备产品"
prop="product"
width="160">
</el-table-column>
<el-table-column
align="center"
label="设备版本"
prop="version"
width="200">
</el-table-column>
<el-table-column
align="center"
label="开发者ID"
prop="developer_id"
width="200">
</el-table-column>
</el-table>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="设备消耗" name="useddevices"> <el-tab-pane label="设备消耗" name="useddevices">
<el-input <el-input
@ -549,7 +620,7 @@
@click="udidDeleteFun(scope)">删除 @click="udidDeleteFun(scope)">删除
</el-button> </el-button>
<el-tag v-else> <el-tag v-else>
...
</el-tag> </el-tag>
</template> </template>
</el-table-column> </el-table-column>
@ -566,7 +637,18 @@
<el-button icon="el-icon-search" type="primary" @click="handleCurrentChange(pagination.currentPage)"> <el-button icon="el-icon-search" type="primary" @click="handleCurrentChange(pagination.currentPage)">
搜索 搜索
</el-button> </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 <el-table
v-loading="loading" v-loading="loading"
@ -605,9 +687,9 @@
<el-table-column <el-table-column
align="center" align="center"
label="设备安装次数" label="次数"
prop="counts" prop="counts"
width="110"> width="100">
</el-table-column> </el-table-column>
@ -644,7 +726,7 @@
<script> <script>
import {developercert, DeviceBillInfo, iosdeveloper, iosdevices, iosdevicesudid} from "@/restful"; import {developercert, DeviceBillInfo, iosdeveloper, iosdevices, iosdevicesudid, iosudevices} from "@/restful";
import {getUserInfoFun, removeAaary} from "@/utils"; import {getUserInfoFun, removeAaary} from "@/utils";
export default { export default {
@ -653,7 +735,10 @@ export default {
return { return {
dialogShowDeviceBillInfo: false, dialogShowDeviceBillInfo: false,
currentudid: '', currentudid: '',
balance_info: {all_balance: 0, used_balance: 0},
bill_percent: 0,
fileList: [], fileList: [],
developer_udevices_lists: [],
app_developer_lists: [], app_developer_lists: [],
app_devices_lists: [], app_devices_lists: [],
app_bill_lists: [], app_bill_lists: [],
@ -687,6 +772,20 @@ export default {
} }
}, },
methods: { methods: {
disabledeveloperFun(developer, act) {
this.$confirm('如您不再使用该开发账户进行签名,可以点击进行禁用,是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.iosdeveloperFun({"methods": "PUT", "data": {"issuer_id": developer.issuer_id, "act": act}});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消禁用操作'
});
});
},
closeDeviceBillInfo() { closeDeviceBillInfo() {
this.dialogShowDeviceBillInfo = false; this.dialogShowDeviceBillInfo = false;
this.currentudid = ''; this.currentudid = '';
@ -796,6 +895,12 @@ export default {
"data": {"issuer_id": this.editdeveloperinfo.issuer_id, "act": "syncdevice"} "data": {"issuer_id": this.editdeveloperinfo.issuer_id, "act": "syncdevice"}
}); });
}, },
syncalldevices() {
this.iosdeveloperFun({
"methods": "PUT",
"data": {"issuer_id": this.appidseach, "act": "syncalldevice"}
});
},
cleandevices() { cleandevices() {
this.iosdeveloperFun({ this.iosdeveloperFun({
"methods": "PUT", "methods": "PUT",
@ -906,6 +1011,8 @@ export default {
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})
} else if (tabname === "iosudevices") {
this.iosudevicesFun("GET", data, null)
} }
}, },
@ -1004,6 +1111,18 @@ export default {
} else { } else {
this.app_bill_lists = data.data; this.app_bill_lists = data.data;
this.pagination.total = data.count; 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 {
this.bill_percent = Number(this.balance_info.used_balance * 100 / this.balance_info.all_balance);
}
}
} }
} else { } else {
this.$message.error("操作失败了 " + data.msg); this.$message.error("操作失败了 " + data.msg);
@ -1044,6 +1163,39 @@ export default {
"methods": action, "data": data "methods": action, "data": data
}) })
}, },
iosudevicesFun(action, data, scope) {
if (action !== 'GET') {
this.loadingfun = this.$loading({
lock: true,
text: '执行中,请耐心等待...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
} else {
this.loading = true
}
iosudevices(data => {
if (data.code === 1000) {
if (action !== "DELETE") {
this.developer_udevices_lists = data.data;
this.pagination.total = data.count;
} else {
if (scope) {
this.app_udid_lists = removeAaary(this.app_udid_lists, scope.row)
}
}
} else {
this.$message.error("操作失败了 " + data.msg);
}
if (action !== 'GET') {
this.loadingfun.close()
} else {
this.loading = false
}
}, {
"methods": action, "data": data
})
},
}, mounted() { }, mounted() {
getUserInfoFun(this); getUserInfoFun(this);
if (this.$route.params.act) { if (this.$route.params.act) {

@ -541,6 +541,22 @@ export function iosdevicesudid(callBack, params, load = true) {
); );
} }
/**超级签名 苹果开发设备udid信息 */
export function iosudevices(callBack, params, load = true) {
// eslint-disable-next-line no-console
getData(
params.methods,
USERSEVER + '/supersign/udevices',
params.data,
data => {
callBack(data);
},
load,
true,
true
);
}
/**获取充值价格信息 */ /**获取充值价格信息 */
export function get_package_prices(callBack, params, load = true) { export function get_package_prices(callBack, params, load = true) {
getData( getData(

@ -21,7 +21,7 @@ from admin.views.login import LoginView, LoginUserView
from admin.views.order import OrderInfoView from admin.views.order import OrderInfoView
from admin.views.report import AdminReportView from admin.views.report import AdminReportView
from admin.views.storage import StorageInfoView, StorageChangeView from admin.views.storage import StorageInfoView, StorageChangeView
from admin.views.supersign import DeveloperInfoView, DevicesInfoView from admin.views.supersign import DeveloperInfoView, DevicesInfoView, SuperSignBillView, SuperSignBillUserInfoView
from admin.views.user import UserInfoView, UserCertificationInfoView, ThirdWxAccountView from admin.views.user import UserInfoView, UserCertificationInfoView, ThirdWxAccountView
urlpatterns = [ urlpatterns = [
@ -39,6 +39,8 @@ urlpatterns = [
re_path("^order/info", OrderInfoView.as_view()), re_path("^order/info", OrderInfoView.as_view()),
re_path("^developer/info", DeveloperInfoView.as_view()), re_path("^developer/info", DeveloperInfoView.as_view()),
re_path("^devices/info", DevicesInfoView.as_view()), re_path("^devices/info", DevicesInfoView.as_view()),
re_path("^bill/info", SuperSignBillView.as_view()),
re_path("^bill/userinfo", SuperSignBillUserInfoView.as_view()),
re_path("^domain/info", DomainNameInfoView.as_view()), re_path("^domain/info", DomainNameInfoView.as_view()),
re_path("^wxbind/info", ThirdWxAccountView.as_view()), re_path("^wxbind/info", ThirdWxAccountView.as_view()),
re_path("^report/info", AdminReportView.as_view()), re_path("^report/info", AdminReportView.as_view()),

@ -10,11 +10,13 @@ from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from api.models import APPSuperSignUsedInfo, AppIOSDeveloperInfo from api.models import APPSuperSignUsedInfo, AppIOSDeveloperInfo, IosDeveloperPublicPoolBill
from api.utils.auth import AdminTokenAuthentication from api.utils.auth import AdminTokenAuthentication
from api.utils.baseutils import get_dict_from_filter_fields from api.utils.baseutils import get_dict_from_filter_fields, get_real_ip_address, get_order_num
from api.utils.modelutils import get_user_public_used_sign_num, get_user_public_sign_num, get_user_obj_from_epu
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.serializer import AdminDeveloperSerializer, AdminSuperSignUsedSerializer from api.utils.serializer import AdminDeveloperSerializer, AdminSuperSignUsedSerializer, AdminBillInfoSerializer
from api.utils.utils import get_developer_devices
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -120,3 +122,90 @@ class DevicesInfoView(APIView):
res.code = 1004 res.code = 1004
res.msg = "数据校验失败" res.msg = "数据校验失败"
return Response(res.dict) return Response(res.dict)
class PageNumber(PageNumberPagination):
page_size = 20 # 每页显示多少条
page_size_query_param = 'limit' # URL中每页显示条数的参数
page_query_param = 'page' # URL中页码的参数
max_page_size = None # 最大页码数限制
class SuperSignBillView(APIView):
authentication_classes = [AdminTokenAuthentication, ]
def get(self, request):
res = BaseResponse()
filter_fields = ["id", "user_id", "to_user_id", "action", "udid", "app_id"]
filter_data = get_dict_from_filter_fields(filter_fields, request.query_params)
sort = request.query_params.get("sort", "-created_time")
page_obj = PageNumber()
obj_list = IosDeveloperPublicPoolBill.objects.filter(**filter_data).order_by(sort)
page_serializer = page_obj.paginate_queryset(queryset=obj_list, request=request,
view=self)
serializer = AdminBillInfoSerializer(page_serializer, many=True)
res.data = serializer.data
res.total = obj_list.count()
return Response(res.dict)
def post(self, request):
res = BaseResponse()
data = request.data
user_id = data.get('user_id')
to_user_id = data.get('to_user_id')
number = data.get('number')
if user_id and number and to_user_id:
user_obj = get_user_obj_from_epu(user_id)
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} 设备数',
)
return Response(res.dict)
except Exception as e:
res.msg = str(e)
else:
res.msg = '用户不合法'
else:
res.msg = '数量和用户不能为空'
res.code = 1001
return Response(res.dict)
def delete(self, request):
res = BaseResponse()
data = request.data
pk = data.get("id", None)
if not pk:
res.code = 1003
res.msg = "参数错误"
else:
order_obj = IosDeveloperPublicPoolBill.objects.filter(pk=pk).first()
order_obj.delete()
return Response(res.dict)
class SuperSignBillUserInfoView(APIView):
authentication_classes = [AdminTokenAuthentication, ]
def get(self, request):
res = BaseResponse()
user_id = request.query_params.get('user_id')
if user_id:
user_obj = get_user_obj_from_epu(user_id)
if user_obj:
res.public_balance_info = {
'used_balance': get_user_public_used_sign_num(user_obj),
'all_balance': get_user_public_sign_num(user_obj)
}
use_num = get_developer_devices(AppIOSDeveloperInfo.objects.filter(user_id=user_obj))
res.private_balance_info = {
'used_balance': use_num.get('flyapp_used_sum', 0) + use_num.get('other_used_sum', 0),
'all_balance': use_num.get('max_total', 0)
}
return Response(res.dict)

@ -26,7 +26,8 @@ from api.views.order import PriceView, OrderView, PaySuccess
from api.views.receiveudids import IosUDIDView, TaskView 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 from api.views.storage import StorageView
from api.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView, SuperSignCertView, DeviceUsedBillView from api.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView, SuperSignCertView, \
DeviceUsedBillView, DeveloperDeviceView
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
@ -57,6 +58,7 @@ urlpatterns = [
re_path("^supersign/developer$", DeveloperView.as_view()), re_path("^supersign/developer$", DeveloperView.as_view()),
re_path("^supersign/devices$", SuperSignUsedView.as_view()), re_path("^supersign/devices$", SuperSignUsedView.as_view()),
re_path("^supersign/udid$", AppUDIDUsedView.as_view()), re_path("^supersign/udid$", AppUDIDUsedView.as_view()),
re_path("^supersign/udevices$", DeveloperDeviceView.as_view()),
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("^package_prices$", PriceView.as_view()), re_path("^package_prices$", PriceView.as_view()),

@ -9,12 +9,12 @@ import logging
import random import random
from urllib.parse import urljoin from urllib.parse import urljoin
from django.db.models import Count, Sum from django.db.models import Count, Sum, Q
from api.models import AppReleaseInfo, UserDomainInfo, DomainCnameInfo, UserAdDisplayInfo, RemoteClientInfo, \ from api.models import AppReleaseInfo, UserDomainInfo, DomainCnameInfo, UserAdDisplayInfo, RemoteClientInfo, \
AppIOSDeveloperInfo, IosDeveloperPublicPoolBill, APPToDeveloper AppIOSDeveloperInfo, IosDeveloperPublicPoolBill, APPToDeveloper, UserInfo
from api.utils.baseutils import get_server_domain_from_request, get_user_default_domain_name, get_real_ip_address, \ from api.utils.baseutils import get_server_domain_from_request, get_user_default_domain_name, get_real_ip_address, \
get_origin_domain_name get_origin_domain_name, is_valid_phone
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -130,20 +130,27 @@ def check_app_domain_name_access(app_obj, access_domain_name, user_obj, extra_do
def get_ios_developer_public_num(user_obj): def get_ios_developer_public_num(user_obj):
add_number = get_user_public_sign_num(user_obj)
if not add_number:
add_number = -99
used_number = get_user_public_used_sign_num(user_obj)
return add_number - used_number
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 = IosDeveloperPublicPoolBill.objects.filter(to_user_id=user_obj, action__in=[1, 2]).aggregate(
number=Sum('number')) 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, used_number = IosDeveloperPublicPoolBill.objects.filter(user_id=user_obj, action=0,
udid_sync_info__isnull=False).values('number', udid_sync_info__isnull=False).values('number',
'udid_sync_info_id').annotate( 'udid_sync_info_id').annotate(
counts=Count('udid_sync_info_id')).aggregate(number=Sum('number')) counts=Count('udid_sync_info_id')).aggregate(number=Sum('number'))
add_number = add_number.get("number", 0) number = used_number.get("number", 0)
if not add_number: return number if number else 0
add_number = -99
used_number = used_number.get("number", 0)
if not used_number:
used_number = 0
return add_number - used_number
def check_super_sign_permission(user_obj): def check_super_sign_permission(user_obj):
@ -167,3 +174,11 @@ def check_ipa_is_latest_sign(app_obj, developer_obj=None):
t_count += 1 t_count += 1
if t_count == all_app_to_dev.count(): if t_count == all_app_to_dev.count():
return True return True
def get_user_obj_from_epu(user_id):
if is_valid_phone(user_id):
user_obj = UserInfo.objects.filter(mobile=user_id).first()
else:
user_obj = UserInfo.objects.filter(Q(email=user_id) | Q(uid=user_id)).first()
return user_obj

@ -549,6 +549,17 @@ class AdminSuperSignUsedSerializer(SuperSignUsedSerializer):
return obj.developerid.pk return obj.developerid.pk
class DeveloperDeviceSerializer(serializers.ModelSerializer):
class Meta:
model = models.UDIDsyncDeveloper
exclude = ["id", "developerid"]
developer_id = serializers.SerializerMethodField()
def get_developer_id(self, obj):
return obj.developerid.issuer_id
class DeviceUDIDSerializer(serializers.ModelSerializer): class DeviceUDIDSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.AppUDID model = models.AppUDID
@ -749,6 +760,12 @@ class BillInfoSerializer(serializers.ModelSerializer):
description = serializers.SerializerMethodField() description = serializers.SerializerMethodField()
is_used = serializers.SerializerMethodField() is_used = serializers.SerializerMethodField()
app_status = 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): def get_app_status(self, obj):
if obj.app_id: if obj.app_id:
@ -779,6 +796,19 @@ class BillInfoSerializer(serializers.ModelSerializer):
return f"{obj.to_user_id.first_name} {self.get_action(obj)} -{obj.number} 设备数" return f"{obj.to_user_id.first_name} {self.get_action(obj)} -{obj.number} 设备数"
class AdminBillInfoSerializer(BillInfoSerializer):
class Meta:
model = models.IosDeveloperPublicPoolBill
fields = '__all__'
read_only_fields = ["id", "user_id", "to_user_id", "action", "number", "app_info", "udid",
"udid_sync_info", "app_id", "remote_addr"]
action_choices = serializers.SerializerMethodField()
def get_action_choices(self, obj):
return get_choices_dict(obj.action_choices)
class AppReportSerializer(serializers.ModelSerializer): class AppReportSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.AppReportInfo model = models.AppReportInfo

@ -41,22 +41,24 @@ def delete_app_to_dev_and_file(developer_obj, app_id):
def get_developer_udided(developer_obj): def get_developer_udided(developer_obj):
super_sing_used_obj = APPSuperSignUsedInfo.objects.filter(developerid=developer_obj).all() super_sing_used_obj = APPSuperSignUsedInfo.objects.filter(developerid=developer_obj).all()
udid_sync_developer_obj = UDIDsyncDeveloper.objects.filter(developerid=developer_obj).all() udid_sync_developer_obj = UDIDsyncDeveloper.objects.filter(developerid=developer_obj).all()
develoer_udid_lists = [] developer_udid_lists = []
supersign_udid_lists = [] super_sign_udid_lists = []
if udid_sync_developer_obj: if udid_sync_developer_obj:
develoer_udid_lists = list(udid_sync_developer_obj.values_list("udid")) developer_udid_lists = list(udid_sync_developer_obj.values_list("udid"))
if super_sing_used_obj: if super_sing_used_obj:
supersign_udid_lists = list(super_sing_used_obj.values_list("udid__udid")) super_sign_udid_lists = list(super_sing_used_obj.values_list("udid__udid__udid"))
return len(set(develoer_udid_lists) - set(supersign_udid_lists)), len(develoer_udid_lists) return len(set(developer_udid_lists) - set(super_sign_udid_lists)), len(set(super_sign_udid_lists))
def get_developer_devices(developer_obj_lists): def get_developer_devices(developer_obj_lists):
other_used_sum = 0 other_used_sum = 0
flyapp_used_sum = 0 flyapp_used_sum = 0
max_total = 0
for dev_obj in developer_obj_lists: for dev_obj in developer_obj_lists:
other_used, flyapp_used = get_developer_udided(dev_obj) other_used, flyapp_used = get_developer_udided(dev_obj)
other_used_sum += other_used other_used_sum += other_used
flyapp_used_sum += flyapp_used flyapp_used_sum += flyapp_used
max_total += 100
use_number_obj_list = developer_obj_lists.filter(is_actived=True) use_number_obj_list = developer_obj_lists.filter(is_actived=True)
all_use_number = 0 all_use_number = 0
@ -70,6 +72,7 @@ def get_developer_devices(developer_obj_lists):
"all_use_number": all_use_number, "all_use_number": all_use_number,
"other_used_sum": other_used_sum, "other_used_sum": other_used_sum,
"flyapp_used_sum": flyapp_used_sum, "flyapp_used_sum": flyapp_used_sum,
"max_total": max_total
} }
return use_num return use_num

@ -5,17 +5,19 @@
# date: 2020/3/4 # date: 2020/3/4
import logging import logging
from django.db.models import Count from django.db.models import Count, Q
from django.http.response import FileResponse from django.http.response import FileResponse
from rest_framework.pagination import PageNumberPagination from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response 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
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.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
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
@ -57,14 +59,25 @@ class DeveloperView(APIView):
def put(self, request): def put(self, request):
data = request.data data = request.data
issuer_id = data.get("issuer_id", None) issuer_id = data.get("issuer_id", "").strip()
if issuer_id: if issuer_id:
developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=request.user, issuer_id=issuer_id).first() developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=request.user, issuer_id=issuer_id).first()
else: else:
act = data.get("act", '').strip()
if act == "syncalldevice":
res = BaseResponse()
result_list = []
for developer_s_obj in AppIOSDeveloperInfo.objects.filter(user_id=request.user, is_actived=True).all():
status, result = IosUtils.get_device_from_developer(developer_s_obj)
if not status:
result_list.append(result.get("err_info"))
if len(result_list):
logger.warning(result_list)
return Response(res.dict)
return self.get(request) return self.get(request)
if developer_obj: if developer_obj:
act = data.get("act", None) act = data.get("act", "").strip()
if act: if act:
res = BaseResponse() res = BaseResponse()
logger.info(f"user {request.user} ios developer {developer_obj} act {act}") logger.info(f"user {request.user} ios developer {developer_obj} act {act}")
@ -106,13 +119,12 @@ class DeveloperView(APIView):
res.code = 1008 res.code = 1008
res.msg = result.get("err_info", '') res.msg = result.get("err_info", '')
return Response(res.dict) return Response(res.dict)
elif act == "syncdevice": elif act in ["syncdevice", "syncalldevice"]:
status, result = IosUtils.get_device_from_developer(developer_obj) status, result = IosUtils.get_device_from_developer(developer_obj)
if not status: if not status:
res.code = 1008 res.code = 1008
res.msg = result.get("err_info") res.msg = result.get("err_info")
return Response(res.dict) return Response(res.dict)
elif act == "cleandevice": elif act == "cleandevice":
if CleanSignDataState.get_state(request.user.uid): if CleanSignDataState.get_state(request.user.uid):
res.code = 1008 res.code = 1008
@ -125,6 +137,10 @@ class DeveloperView(APIView):
res.code = 1008 res.code = 1008
res.msg = result.get("err_info") res.msg = result.get("err_info")
return Response(res.dict) return Response(res.dict)
elif act == 'disable':
developer_obj.is_actived = False
developer_obj.save(update_fields=['is_actived'])
return self.get(request)
else: else:
update_fields = [] update_fields = []
logger.info(f"user {request.user} ios developer {developer_obj} update input data {data}") logger.info(f"user {request.user} ios developer {developer_obj} update input data {data}")
@ -273,6 +289,47 @@ class AppUDIDUsedView(APIView):
return Response(res.dict) return Response(res.dict)
class DeveloperDeviceView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
permission_classes = [SuperSignPermission, ]
def get(self, request):
res = BaseResponse()
udid = request.query_params.get("udid", None)
developer_id = request.query_params.get("appid", None)
super_sign_used_objs = UDIDsyncDeveloper.objects.filter(developerid__user_id=request.user, )
if developer_id:
super_sign_used_objs = super_sign_used_objs.filter(developerid__issuer_id=developer_id)
if udid:
super_sign_used_objs = super_sign_used_objs.filter(udid=udid)
page_obj = PageNumber()
app_page_serializer = page_obj.paginate_queryset(queryset=super_sign_used_objs.order_by('-id'),
request=request,
view=self)
app_serializer = DeveloperDeviceSerializer(app_page_serializer, many=True)
res.data = app_serializer.data
res.count = super_sign_used_objs.count()
return Response(res.dict)
def delete(self, request):
res = BaseResponse()
pk = request.query_params.get("id", None)
app_id = request.query_params.get("aid", None)
app_udid_obj = AppUDID.objects.filter(pk=pk)
if app_udid_obj:
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:
logger.error(f"user {request.user} delete devices {app_udid_obj}")
IosUtils.disable_udid(app_udid_obj.first(), app_id)
app_udid_obj.delete()
else:
res.code = 10002
res.msg = '公共账号池不允许删除'
return Response(res.dict)
class SuperSignCertView(APIView): class SuperSignCertView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ] authentication_classes = [ExpiringTokenAuthentication, ]
permission_classes = [SuperSignPermission, ] permission_classes = [SuperSignPermission, ]
@ -325,7 +382,8 @@ class DeviceUsedBillView(APIView):
res = BaseResponse() res = BaseResponse()
udid = request.query_params.get("udid", None) udid = request.query_params.get("udid", None)
act = request.query_params.get("act", None) act = request.query_params.get("act", None)
user_used_list = IosDeveloperPublicPoolBill.objects.filter(user_id=request.user) user_used_list = IosDeveloperPublicPoolBill.objects.filter(
Q(to_user_id=request.user) | Q(user_id=request.user))
page_obj = PageNumber() page_obj = PageNumber()
if udid: if udid:
user_used_list = user_used_list.filter(udid=udid) user_used_list = user_used_list.filter(udid=udid)
@ -340,13 +398,19 @@ class DeviceUsedBillView(APIView):
res.count = user_used_list.count() res.count = user_used_list.count()
else: else:
if udid:
user_used_list = user_used_list.filter(udid=udid)
user_used_list3 = user_used_list.values('udid', 'product', 'version', 'udid_sync_info_id').annotate( user_used_list3 = user_used_list.values('udid', 'product', 'version', 'udid_sync_info_id').annotate(
counts=Count('udid')).order_by("-created_time") counts=Count('udid'))
res.count = user_used_list3.count() res.count = user_used_list3.count()
user_used_list3 = page_obj.paginate_queryset(queryset=user_used_list3.order_by("-created_time"), user_used_list3 = page_obj.paginate_queryset(queryset=user_used_list3.order_by('-udid_sync_info_id'),
request=request, request=request, view=self)
view=self)
for user_used in user_used_list3: for user_used in user_used_list3:
user_used['counts'] = user_used_list.filter(udid=user_used.get('udid')).count() user_used['counts'] = user_used_list.filter(udid=user_used.get('udid')).count()
res.data = user_used_list3 res.data = user_used_list3
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) return Response(res.dict)

Loading…
Cancel
Save