增加管理后台应用版本历史记录功能

pull/16/head
youngS 4 years ago
parent 23a215d980
commit cae0715e7a
  1. 20
      fir_admin/src/api/app.js
  2. 14
      fir_admin/src/router/index.js
  3. 6
      fir_admin/src/views/appinfos/AppDetail.vue
  4. 195
      fir_admin/src/views/appinfos/AppReleaseDetail.vue
  5. 167
      fir_admin/src/views/appinfos/AppReleaseList.vue
  6. 23
      fir_admin/src/views/appinfos/list.vue
  7. 14
      fir_admin/src/views/userinfos/UserDetail.vue
  8. 28
      fir_admin/src/views/userinfos/list.vue
  9. 2
      fir_client/src/components/user/FirUserProfileCertification.vue
  10. 5
      fir_ser/admin/urls.py
  11. 35
      fir_ser/admin/views/app.py
  12. 7
      fir_ser/admin/views/user.py
  13. 2
      fir_ser/api/models.py
  14. 35
      fir_ser/api/utils/serializer.py

@ -2,7 +2,7 @@ import request from '@/utils/request'
export function getAppInfos(query) { export function getAppInfos(query) {
return request({ return request({
url: '/appinfo', url: '/app/info',
method: 'get', method: 'get',
params: query params: query
}) })
@ -10,7 +10,23 @@ export function getAppInfos(query) {
export function updateAppInfo(data) { export function updateAppInfo(data) {
return request({ return request({
url: '/appinfo', url: '/app/info',
method: 'put',
data
})
}
export function getAppReleaseInfos(query) {
return request({
url: '/app/release/info',
method: 'get',
params: query
})
}
export function updateReleaseAppInfo(data) {
return request({
url: '/app/release/info',
method: 'put', method: 'put',
data data
}) })

@ -102,6 +102,20 @@ export const constantRoutes = [
name: 'app_info_edit', name: 'app_info_edit',
meta: { title: '编辑信息', noCache: true, activeMenu: '/apps/list' }, meta: { title: '编辑信息', noCache: true, activeMenu: '/apps/list' },
hidden: true hidden: true
},
{
path: 'release/:app_id(\\d+)/list',
name: 'app_release_info_list',
component: () => import('@/views/appinfos/AppReleaseList'),
meta: { title: '版本记录', noCache: true, activeMenu: '/apps/list' },
hidden: true
},
{
path: 'release/:app_id(\\d+)/edit/:id(\\d+)',
name: 'app_release_info_edit',
component: () => import('@/views/appinfos/AppReleaseDetail'),
meta: { title: '版本记录', noCache: true, activeMenu: '/apps/list' },
hidden: true
} }
] ]
}, },

@ -297,9 +297,3 @@ export default {
} }
</script> </script>
<style lang="scss" scoped>
.el-form-item__label {
width: 200px;
}
</style>

@ -0,0 +1,195 @@
<template>
<div class="app-container">
<el-form ref="postForm" :model="postForm" label-width="100px" :disabled="!is_edit">
<el-row>
<el-col :span="12">
<el-form-item label="Release_ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input :value="postForm.release_id" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="build版本">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.build_version" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="app版本">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.app_version" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="应用大小">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.binary_size" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="版本类型">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.release_type" class="filter-item" placeholder="Please select" disabled>
<el-option v-for="item in postForm.release_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="创建时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.created_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="更新日志">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.changelog" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" placeholder="Please input" />
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="应用图标" label-width="200px">
<el-row :gutter="12">
<el-col :span="16">
<el-image :src="postForm.icon_url" :preview-src-list="[postForm.icon_url]" fit="contain" style="width: 100px; height: 100px" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="是否master版本" label-width="200px">
<el-row :gutter="12">
<el-col :span="16">
<el-tooltip :content="postForm.is_master|statusFilter" placement="top">
<el-switch
v-model="postForm.is_master"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="true"
:inactive-value="false"
/>
</el-tooltip>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="企业签名名称" label-width="200px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.distribution_name" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="第三方下载URL" label-width="200px">
<el-row :gutter="12">
<el-col :span="16">
<el-input :value="postForm.binary_url" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="应用可安装的最低系统版本" label-width="200px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.minimum_os_version" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="ios内测版 udid" label-width="200px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.udid" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" disabled />
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-button v-if="!is_edit" type="primary" @click="is_edit=true">修改</el-button>
<div v-else>
<el-button type="primary" @click="is_edit=false">取消</el-button>
<el-button type="primary" @click="updateData">保存修改</el-button>
</div>
</el-col>
</div>
</template>
<script>
import { getAppReleaseInfos, updateReleaseAppInfo } from '@/api/app'
const defaultForm = {
is_master: undefined,
udid: undefined,
distribution_name: undefined,
release_id: undefined,
build_version: undefined,
app_version: undefined,
release_type: undefined,
binary_size: undefined,
icon_url: undefined,
binary_url: undefined,
changelog: undefined,
created_time: undefined,
release_choices: undefined,
minimum_os_version: undefined
}
export default {
name: 'AppReleaseDetail',
components: { }, filters: {
userStatusFilter(status) {
const statusMap = {
true: '激活,允许登录',
false: '禁用,禁止登录'
}
return statusMap[status]
},
statusFilter(status) {
const statusMap = {
true: '启用,允许配置',
false: '禁用,禁止配置'
}
return statusMap[status]
}
},
data() {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false
}
},
computed: {
},
created() {
const id = this.$route.params && this.$route.params.id
const app_id = this.$route.params && this.$route.params.app_id
this.fetchData(id, app_id)
},
methods: {
fetchData(id, app_id) {
getAppReleaseInfos({ id: id, app_id: app_id }).then(response => {
if (response.data.length === 1) {
this.postForm = response.data[0]
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updateReleaseAppInfo(this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {
console.log(err)
})
}
}
}
</script>

@ -0,0 +1,167 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="listQuery.release_id" placeholder="release_id" style="width: 250px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<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="应用图标" align="center" width="130">
<template slot-scope="scope">
<el-image :src="scope.row.icon_url" :preview-src-list="[scope.row.icon_url]" fit="contain" style="width: 80px; height: 80px" />
</template>
</el-table-column>
<el-table-column label="Release_Id" align="center">
<template slot-scope="scope">
<span>{{ scope.row.release_id }}</span>
</template>
</el-table-column>
<el-table-column label="第三方下载URL" align="center">
<template slot-scope="scope">
{{ scope.row.binary_url }}
</template>
</el-table-column>
<el-table-column label="更新日志" align="center">
<template slot-scope="scope">
{{ scope.row.changelog }}
</template>
</el-table-column>
<el-table-column class-name="status-col" label="Master" width="120" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.is_master | statusFilter">{{ scope.row.is_master }}</el-tag>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="版本类型" width="120" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.release_type | appStatusFilter">{{ scope.row |appStatusNameFilter }}</el-tag>
</template>
</el-table-column>
<el-table-column label="应用大小" align="center" width="80">
<template slot-scope="scope">
<span>{{ scope.row.binary_size }}</span>
</template>
</el-table-column>
<el-table-column align="center" prop="created_time" label="上传时间" width="180">
<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="100" class-name="small-padding fixed-width">
<template slot-scope="scope">
<router-link :to="{name: 'app_release_info_edit',params:{app_id:listQuery.app_id,id:scope.row.id}}">
<el-button type="primary" size="mini">
查看编辑
</el-button>
</router-link>
</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 { getAppReleaseInfos } from '@/api/app'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '上传时间 Ascending', key: 'created_time' },
{ label: '上传时间 Descending', key: '-created_time' },
]
export default {
name: 'AppInfo',
components: { Pagination },
directives: { waves },
filters: {
formatTime(time) {
return time.replace('T', ' ').split('.')[0]
},
statusFilter(status) {
const statusMap = {
true: 'success',
false: 'danger'
}
return statusMap[status]
},
appStatusNameFilter(row) {
for (const r of row.release_choices) {
if (r.id === row.release_type) {
return r.name
}
}
},
appStatusFilter(status) {
const statusMap = {
'0': 'info',
'1': 'success',
'2': 'gray',
'3': 'danger',
}
return statusMap[status]
}
},
data() {
return {
list: null,
listLoading: true,
total: 0,
listQuery: {
page: 1,
limit: 10,
app_id: undefined,
release_id: undefined,
sort: '-created_time',
},
sortOptions,
type_choices: [],
status_choices: []
}
},
created() {
this.listQuery.app_id = this.$route.params && this.$route.params.app_id
this.fetchData()
},
methods: {
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
fetchData() {
this.listLoading = true
getAppReleaseInfos(this.listQuery).then(response => {
this.list = response.data
if (this.list && this.list.length > 0) {
this.type_choices = this.list[0].type_choices
this.status_choices = this.list[0].status_choices
}
this.total = response.total
this.listLoading = false
})
}
}
}
</script>

@ -33,7 +33,7 @@
{{ scope.row.id }} {{ scope.row.id }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="图标" align="center" width="130"> <el-table-column label="应用图标" align="center" width="130">
<template slot-scope="scope"> <template slot-scope="scope">
<el-image :src="scope.row.master_release.icon_url" :preview-src-list="[scope.row.master_release.icon_url]" fit="contain" style="width: 80px; height: 80px" /> <el-image :src="scope.row.master_release.icon_url" :preview-src-list="[scope.row.master_release.icon_url]" fit="contain" style="width: 80px; height: 80px" />
</template> </template>
@ -76,6 +76,18 @@
{{ scope.row.count_hits }} {{ scope.row.count_hits }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="应用大小" width="80" align="center">
<template slot-scope="scope">
{{ scope.row.master_release.binary_size }}
</template>
</el-table-column>
<el-table-column label="历史版本数" width="100" align="center">
<template slot-scope="scope">
<router-link :to="{name: 'app_release_info_list',params:{app_id:scope.row.id}}">
<el-link type="primary"> {{ scope.row.release_count }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="应用状态" width="80" align="center"> <el-table-column class-name="status-col" label="应用状态" width="80" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag :type="scope.row.status | appStatusFilter">{{ scope.row |appStatusNameFilter }}</el-tag> <el-tag :type="scope.row.status | appStatusFilter">{{ scope.row |appStatusNameFilter }}</el-tag>
@ -96,13 +108,16 @@
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" width="100" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope"> <template slot-scope="scope">
<router-link :to="{name: 'app_info_edit',params:{id:scope.row.id}}"> <router-link :to="{name: 'app_info_edit',params:{id:scope.row.id}}">
<el-button type="primary" size="mini" icon="el-icon-edit"> <el-button type="primary" size="mini">
编辑 查看编辑
</el-button> </el-button>
</router-link> </router-link>
<el-button type="danger" size="mini">
删除
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>

@ -50,6 +50,18 @@
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="实名认证">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.certification" class="filter-item" placeholder="Please select" :disabled="postForm.certification === -1">
<el-option v-for="item in postForm.certification_status_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
<el-col v-if="postForm.certification === -1" span="16">
<el-link :underline="false"> 用户需要先提交认证信息才可以进行认证修改</el-link>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="手机" prop="mobile"> <el-form-item label="手机" prop="mobile">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
@ -305,6 +317,7 @@ export default {
source_uri: [{ validator: validateSourceUri, trigger: 'blur' }] source_uri: [{ validator: validateSourceUri, trigger: 'blur' }]
}, },
is_edit: false, is_edit: false,
certification_status_choices: []
} }
}, },
computed: { computed: {
@ -320,6 +333,7 @@ export default {
getUserInfos({ id: id }).then(response => { getUserInfos({ id: id }).then(response => {
if (response.data.length === 1) { if (response.data.length === 1) {
this.postForm = response.data[0] this.postForm = response.data[0]
this.certification_status_choices = this.postForm.certification_status_choices
} }
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)

@ -6,7 +6,7 @@
<el-input v-model="listQuery.mobile" placeholder="手机" style="width: 150px;" class="filter-item" clearable @keyup.enter.native="handleFilter" /> <el-input v-model="listQuery.mobile" placeholder="手机" style="width: 150px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.email" placeholder="邮箱" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" /> <el-input v-model="listQuery.email" placeholder="邮箱" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.certification" placeholder="实名认证状态" clearable class="filter-item" style="width: 140px" @change="handleFilter"> <el-select v-model="listQuery.certification" placeholder="实名认证状态" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in searchCertficationOptions" :key="item.key" :label="item.display_name" :value="item.key" /> <el-option v-for="item in certification_status_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter"> <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-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
@ -79,7 +79,7 @@
</el-table-column> </el-table-column>
<el-table-column class-name="status-col" label="实名认证" width="95" align="center"> <el-table-column class-name="status-col" label="实名认证" width="95" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag :type="scope.row.certification | certStatusFilter">{{ scope.row.certification| certLableFilter }}</el-tag> <el-tag :type="scope.row.certification | certStatusFilter">{{ scope.row| certLableFilter }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
@ -94,8 +94,8 @@
<el-table-column label="操作" align="center" width="100" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" width="100" class-name="small-padding fixed-width">
<template slot-scope="scope"> <template slot-scope="scope">
<router-link :to="{name: 'user_info_edit',params:{id:scope.row.id}}"> <router-link :to="{name: 'user_info_edit',params:{id:scope.row.id}}">
<el-button type="primary" size="mini" icon="el-icon-edit"> <el-button type="primary" size="mini">
编辑 查看编辑
</el-button> </el-button>
</router-link> </router-link>
</template> </template>
@ -111,13 +111,6 @@ import { getUserInfos } from '@/api/user'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive import waves from '@/directive/waves' // waves directive
const searchCertficationOptions = [
{ key: '-1', display_name: '未认证' },
{ key: '0', display_name: '认证中' },
{ key: '1', display_name: '认证成功' },
{ key: '2', display_name: '认证失败' }
]
const sortOptions = [ const sortOptions = [
{ label: '注册时间 Ascending', key: 'date_joined' }, { label: '注册时间 Ascending', key: 'date_joined' },
{ label: '注册时间 Descending', key: '-date_joined' }, { label: '注册时间 Descending', key: '-date_joined' },
@ -149,10 +142,10 @@ export default {
} }
return statusMap[status] return statusMap[status]
}, },
certLableFilter(status) { certLableFilter(row) {
for (const v of searchCertficationOptions) { for (const v of row.certification_status_choices) {
if (v.key === status.toString()) { if (v.id === row.certification) {
return v.display_name return v.name
} }
} }
} }
@ -172,7 +165,7 @@ export default {
mobile: undefined mobile: undefined
}, },
sortOptions, sortOptions,
searchCertficationOptions certification_status_choices: []
} }
}, },
created() { created() {
@ -188,6 +181,9 @@ export default {
getUserInfos(this.listQuery).then(response => { getUserInfos(this.listQuery).then(response => {
this.list = response.data this.list = response.data
this.total = response.total this.total = response.total
if (this.list && this.list.length > 0) {
this.certification_status_choices = this.list[0].certification_status_choices
}
this.listLoading = false this.listLoading = false
}) })
} }

@ -76,7 +76,7 @@
<el-form-item label="真实姓名"> <el-form-item label="真实姓名">
<el-row :gutter="36"> <el-row :gutter="36">
<el-col :span="18"> <el-col :span="18">
<el-input v-model="form.name" autofocus="true" prefix-icon="el-icon-user" <el-input v-model="form.name" :autofocus="true" prefix-icon="el-icon-user"
placeholder="请输入真实姓名" clearable/> placeholder="请输入真实姓名" clearable/>
</el-col> </el-col>
</el-row> </el-row>

@ -16,7 +16,7 @@ Including another URLconf
from django.urls import re_path from django.urls import re_path
from admin.views.login import LoginView, LoginUserView from admin.views.login import LoginView, LoginUserView
from admin.views.user import UserInfoView from admin.views.user import UserInfoView
from admin.views.app import AppInfoView from admin.views.app import AppInfoView, AppReleaseInfoView
urlpatterns = [ urlpatterns = [
# path("",include(router.urls)), # path("",include(router.urls)),
@ -25,6 +25,7 @@ urlpatterns = [
re_path("^login", LoginView.as_view()), re_path("^login", LoginView.as_view()),
re_path("^user/info", LoginUserView.as_view()), re_path("^user/info", LoginUserView.as_view()),
re_path("^userinfo", UserInfoView.as_view()), re_path("^userinfo", UserInfoView.as_view()),
re_path("^appinfo", AppInfoView.as_view()), re_path("^app/info", AppInfoView.as_view()),
re_path("^app/release/info", AppReleaseInfoView.as_view()),
] ]

@ -55,14 +55,14 @@ class AppInfoView(APIView):
def put(self, request): def put(self, request):
res = BaseResponse() res = BaseResponse()
data = request.data data = request.data
id = data.get("id", None) pk = data.get("id", None)
if not id: if not pk:
res.code = 1003 res.code = 1003
res.msg = "参数错误" res.msg = "参数错误"
return Response(res.dict) return Response(res.dict)
app_obj = Apps.objects.filter(id=id).first() app_obj = Apps.objects.filter(pk=pk).first()
if app_obj: if app_obj:
data['pk'] = id data['pk'] = pk
serializer_obj = AdminAppsSerializer(app_obj, data=data, partial=True) serializer_obj = AdminAppsSerializer(app_obj, data=data, partial=True)
if serializer_obj.is_valid(): if serializer_obj.is_valid():
serializer_obj.save() serializer_obj.save()
@ -73,6 +73,16 @@ class AppInfoView(APIView):
res.msg = "数据校验失败" res.msg = "数据校验失败"
return Response(res.dict) 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 = "参数错误"
Apps.delete()
return Response(res.dict)
class AppReleaseInfoView(APIView): class AppReleaseInfoView(APIView):
authentication_classes = [AdminTokenAuthentication, ] authentication_classes = [AdminTokenAuthentication, ]
@ -80,12 +90,16 @@ class AppReleaseInfoView(APIView):
def get(self, request): def get(self, request):
res = BaseResponse() res = BaseResponse()
filter_data = {} filter_data = {}
filter_fields = ["id", "release_id"] filter_fields = ["id", "release_id", "app_id"]
for filed in filter_fields: for filed in filter_fields:
f_value = request.query_params.get(filed, None) f_value = request.query_params.get(filed, None)
if f_value: if f_value:
filter_data[filed] = f_value filter_data[filed] = f_value
sort = request.query_params.get("sort", "-created_time") sort = request.query_params.get("sort", "-created_time")
if not filter_data.get('app_id', None):
res.code = 1003
res.msg = "参数错误"
return Response(res.dict)
page_obj = AppsPageNumber() page_obj = AppsPageNumber()
obj_list = AppReleaseInfo.objects.filter(**filter_data).order_by(sort) obj_list = AppReleaseInfo.objects.filter(**filter_data).order_by(sort)
page_serializer = page_obj.paginate_queryset(queryset=obj_list, request=request, page_serializer = page_obj.paginate_queryset(queryset=obj_list, request=request,
@ -98,19 +112,20 @@ class AppReleaseInfoView(APIView):
def put(self, request): def put(self, request):
res = BaseResponse() res = BaseResponse()
data = request.data data = request.data
id = data.get("id", None) pk = data.get("id", None)
if not id: app_id = data.get("app_id", None)
if not pk or not app_id:
res.code = 1003 res.code = 1003
res.msg = "参数错误" res.msg = "参数错误"
return Response(res.dict) return Response(res.dict)
app_obj = Apps.objects.filter(id=id).first() app_obj = AppReleaseInfo.objects.filter(pk=pk, app_id=app_id).first()
if app_obj: if app_obj:
data['pk'] = id data['pk'] = pk
serializer_obj = AdminAppReleaseSerializer(app_obj, data=data, partial=True) serializer_obj = AdminAppReleaseSerializer(app_obj, data=data, partial=True)
if serializer_obj.is_valid(): if serializer_obj.is_valid():
serializer_obj.save() serializer_obj.save()
res.data = serializer_obj.data res.data = serializer_obj.data
del_cache_response_by_short(app_obj.app_id) del_cache_response_by_short(app_obj.app_id.app_id)
return Response(res.dict) return Response(res.dict)
res.code = 1004 res.code = 1004
res.msg = "数据校验失败" res.msg = "数据校验失败"

@ -5,7 +5,7 @@
# date: 2021/4/11 # date: 2021/4/11
from django.contrib import auth from django.contrib import auth
from api.models import Token, UserInfo from api.models import Token, UserInfo, UserCertificationInfo
from rest_framework.response import Response from rest_framework.response import Response
from api.utils.auth import AdminTokenAuthentication from api.utils.auth import AdminTokenAuthentication
from api.utils.serializer import AdminUserInfoSerializer from api.utils.serializer import AdminUserInfoSerializer
@ -37,7 +37,7 @@ class UserInfoView(APIView):
def get(self, request): def get(self, request):
res = BaseResponse() res = BaseResponse()
filter_data = {} filter_data = {}
filter_fileds = ["id", "mobile", "username", "email", "first_name", "certification", ] filter_fileds = ["id", "mobile", "username", "email", "first_name"]
for filed in filter_fileds: for filed in filter_fileds:
f_value = request.query_params.get(filed, None) f_value = request.query_params.get(filed, None)
if f_value: if f_value:
@ -72,6 +72,9 @@ class UserInfoView(APIView):
users_serializer = AdminUserInfoSerializer(user_obj, data=data, partial=True) users_serializer = AdminUserInfoSerializer(user_obj, data=data, partial=True)
if users_serializer.is_valid(): if users_serializer.is_valid():
users_serializer.save() users_serializer.save()
certification = data.get("certification", None)
if certification and certification != -1:
UserCertificationInfo.objects.filter(user_id=user_obj).update(status=data["certification"])
res.data = users_serializer.data res.data = users_serializer.data
return Response(res.dict) return Response(res.dict)
res.code = 1004 res.code = 1004

@ -405,7 +405,7 @@ class UserCertificationInfo(models.Model):
card = models.CharField(max_length=128, null=False, verbose_name="身份证号码") card = models.CharField(max_length=128, null=False, verbose_name="身份证号码")
addr = models.CharField(max_length=128, null=False, verbose_name="居住地") addr = models.CharField(max_length=128, null=False, verbose_name="居住地")
mobile = models.BigIntegerField(verbose_name="手机号码", unique=True, null=False) mobile = models.BigIntegerField(verbose_name="手机号码", unique=True, null=False)
status_choices = ((0, '审核'), (1, '审核成功'), (2, '审核失败')) status_choices = ((-1, '待认证'),(0, '认证'), (1, '认证成功'), (2, '认证失败'))
status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="审核状态") status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="审核状态")
msg = models.CharField(max_length=512, null=True, blank=True, verbose_name="备注") msg = models.CharField(max_length=512, null=True, blank=True, verbose_name="备注")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")

@ -20,7 +20,12 @@ def get_download_url_from_context(self, obj, key, url, force_new=False):
if self.context.get("storage", None) and self.context.get("storage") != "undefined": if self.context.get("storage", None) and self.context.get("storage") != "undefined":
storage = self.context.get("storage", None) storage = self.context.get("storage", None)
else: else:
if isinstance(obj, models.Apps):
storage = Storage(obj.user_id) storage = Storage(obj.user_id)
elif isinstance(obj, models.AppReleaseInfo):
storage = Storage(obj.app_id.user_id)
else:
storage = None
if storage: if storage:
icon_url = storage.get_download_url(os.path.basename(url), 600, key, force_new) icon_url = storage.get_download_url(os.path.basename(url), 600, key, force_new)
return icon_url return icon_url
@ -75,7 +80,7 @@ class AdminUserInfoSerializer(UserInfoSerializer):
class Meta: class Meta:
model = models.UserInfo model = models.UserInfo
exclude = ["password", "api_token"] exclude = ["password", "api_token"]
read_only_fields = ["id", "head_img", "free_download_times", "certification", "last_login", read_only_fields = ["id", "head_img", "free_download_times", "last_login",
"is_superuser", "last_name", "is_staff", "uid", "storage_active", "supersign_active", "is_superuser", "last_name", "is_staff", "uid", "storage_active", "supersign_active",
"date_joined", "download_times", "all_download_times", "storage", "groups", "date_joined", "download_times", "all_download_times", "storage", "groups",
"user_permissions"] "user_permissions"]
@ -95,6 +100,11 @@ class AdminUserInfoSerializer(UserInfoSerializer):
def get_storage_choices(self, obj): def get_storage_choices(self, obj):
return get_choices_dict(models.AppStorage.storage_choices[1:]) return get_choices_dict(models.AppStorage.storage_choices[1:])
certification_status_choices = serializers.SerializerMethodField()
def get_certification_status_choices(self, obj):
return get_choices_dict(models.UserCertificationInfo.status_choices)
def update(self, instance, validated_data): def update(self, instance, validated_data):
return super(AdminUserInfoSerializer, self).update(instance, validated_data) return super(AdminUserInfoSerializer, self).update(instance, validated_data)
@ -171,7 +181,7 @@ class AdminAppsSerializer(AppsSerializer):
class Meta: class Meta:
model = models.Apps model = models.Apps
fields = "__all__" fields = "__all__"
read_only_fields = ["id", "app_id", "user_id", "bundle_id", "count_hits","updated_time","created_time"] read_only_fields = ["id", "app_id", "user_id", "bundle_id", "count_hits", "updated_time", "created_time"]
status_choices = serializers.SerializerMethodField() status_choices = serializers.SerializerMethodField()
@ -188,10 +198,15 @@ class AdminAppsSerializer(AppsSerializer):
def get_supersign_type_choices(self, obj): def get_supersign_type_choices(self, obj):
return get_choices_dict(obj.supersign_type_choices) return get_choices_dict(obj.supersign_type_choices)
release_count = serializers.SerializerMethodField()
def get_release_count(self, obj):
return models.AppReleaseInfo.objects.filter(app_id=obj).count()
def update(self, instance, validated_data): def update(self, instance, validated_data):
print(validated_data)
return super(AdminAppsSerializer, self).update(instance, validated_data) return super(AdminAppsSerializer, self).update(instance, validated_data)
class AppsShortSerializer(serializers.ModelSerializer): class AppsShortSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.Apps model = models.Apps
@ -224,7 +239,8 @@ class AppsShortSerializer(serializers.ModelSerializer):
master_release_obj = get_app_master_obj_from_context(self, obj) master_release_obj = get_app_master_obj_from_context(self, obj)
if master_release_obj: if master_release_obj:
key = '' key = ''
icon_url = get_download_url_from_context(self, obj, key, os.path.basename(master_release_obj.icon_url), True) icon_url = get_download_url_from_context(self, obj, key, os.path.basename(master_release_obj.icon_url),
True)
datainfo = { datainfo = {
"app_version": master_release_obj.app_version, "app_version": master_release_obj.app_version,
"icon_url": icon_url, "icon_url": icon_url,
@ -296,9 +312,9 @@ class AppReleaseSerializer(serializers.ModelSerializer):
class AdminAppReleaseSerializer(AppReleaseSerializer): class AdminAppReleaseSerializer(AppReleaseSerializer):
class Meta: class Meta:
model = models.Apps model = models.AppReleaseInfo
fields = "__all__" fields = "__all__"
read_only_fields = ["id", "app_id", "release_id", "is_master"] read_only_fields = ["id", "app_id", "release_id", "binary_size"]
release_choices = serializers.SerializerMethodField() release_choices = serializers.SerializerMethodField()
@ -306,8 +322,15 @@ class AdminAppReleaseSerializer(AppReleaseSerializer):
return get_choices_dict(obj.release_choices) return get_choices_dict(obj.release_choices)
def update(self, instance, validated_data): def update(self, instance, validated_data):
print(validated_data)
if validated_data.get("is_master", False):
models.AppReleaseInfo.objects.filter(app_id=instance.app_id).update(**{"is_master": False})
else:
if "is_master" in validated_data and validated_data.get("is_master") != True:
del validated_data["is_master"]
return super(AdminAppReleaseSerializer, self).update(instance, validated_data) return super(AdminAppReleaseSerializer, self).update(instance, validated_data)
class StorageSerializer(serializers.ModelSerializer): class StorageSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.AppStorage model = models.AppStorage

Loading…
Cancel
Save