diff --git a/fir_admin/src/api/storage.js b/fir_admin/src/api/storage.js new file mode 100644 index 0000000..e8d754d --- /dev/null +++ b/fir_admin/src/api/storage.js @@ -0,0 +1,18 @@ +import request from '@/utils/request' + +export function getStorageInfo(query) { + return request({ + url: '/storage/info', + method: 'get', + params: query + }) +} + +export function updateStorageInfo(data) { + return request({ + url: '/storage/info', + method: 'put', + data + }) +} + diff --git a/fir_admin/src/api/user.js b/fir_admin/src/api/user.js index 900f5e2..abd6727 100644 --- a/fir_admin/src/api/user.js +++ b/fir_admin/src/api/user.js @@ -38,3 +38,18 @@ export function updateUserInfo(data) { data }) } + +export function getCertificationInfo(query) { + return request({ + url: '/certification/info', + method: 'get', + params: query + }) +} +export function updateCertificationInfo(data) { + return request({ + url: '/certification/info', + method: 'put', + data + }) +} diff --git a/fir_admin/src/router/index.js b/fir_admin/src/router/index.js index 8a30e93..ef618c0 100644 --- a/fir_admin/src/router/index.js +++ b/fir_admin/src/router/index.js @@ -124,10 +124,17 @@ export const constantRoutes = [ component: Layout, children: [ { - path: 'index', - name: 'index', - component: () => import('@/views/form/index'), + path: 'list', + name: 'storage_info_list', + component: () => import('@/views/storage/list'), meta: { title: '存储管理', icon: 'form' } + }, + { + path: 'edit/:id(\\d+)', + component: () => import('@/views/storage/Detail'), + name: 'storage_info_edit', + meta: { title: '编辑信息', noCache: true, activeMenu: '/storage/list' }, + hidden: true } ] }, @@ -143,14 +150,21 @@ export const constantRoutes = [ } ] }, { - path: '/certification', + path: '/authentication', component: Layout, children: [ { - path: 'index', - name: 'index', - component: () => import('@/views/form/index'), + path: 'list', + name: 'user_authentication_info_list', + component: () => import('@/views/authentication/list'), meta: { title: '用户实名认证', icon: 'form' } + }, + { + path: 'edit/:id(\\d+)', + component: () => import('@/views/authentication/Detail'), + name: 'user_authentication_info_edit', + meta: { title: '编辑信息', noCache: true, activeMenu: '/authentication/list' }, + hidden: true } ] }, { diff --git a/fir_admin/src/views/appinfos/AppReleaseList.vue b/fir_admin/src/views/appinfos/AppReleaseList.vue index cbcf6b5..083eb03 100644 --- a/fir_admin/src/views/appinfos/AppReleaseList.vue +++ b/fir_admin/src/views/appinfos/AppReleaseList.vue @@ -89,7 +89,7 @@ import waves from '@/directive/waves' // waves directive const sortOptions = [ { label: '上传时间 Ascending', key: 'created_time' }, - { label: '上传时间 Descending', key: '-created_time' }, + { label: '上传时间 Descending', key: '-created_time' } ] export default { @@ -119,7 +119,7 @@ export default { '0': 'info', '1': 'success', '2': 'gray', - '3': 'danger', + '3': 'danger' } return statusMap[status] } @@ -134,7 +134,7 @@ export default { limit: 10, app_id: undefined, release_id: undefined, - sort: '-created_time', + sort: '-created_time' }, sortOptions, type_choices: [], diff --git a/fir_admin/src/views/appinfos/Dropdown/Comment.vue b/fir_admin/src/views/appinfos/Dropdown/Comment.vue deleted file mode 100644 index d34b2b9..0000000 --- a/fir_admin/src/views/appinfos/Dropdown/Comment.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - diff --git a/fir_admin/src/views/appinfos/Dropdown/Platform.vue b/fir_admin/src/views/appinfos/Dropdown/Platform.vue deleted file mode 100644 index 0a52726..0000000 --- a/fir_admin/src/views/appinfos/Dropdown/Platform.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - diff --git a/fir_admin/src/views/appinfos/Dropdown/SourceUrl.vue b/fir_admin/src/views/appinfos/Dropdown/SourceUrl.vue deleted file mode 100644 index f514e2a..0000000 --- a/fir_admin/src/views/appinfos/Dropdown/SourceUrl.vue +++ /dev/null @@ -1,38 +0,0 @@ -v - - diff --git a/fir_admin/src/views/appinfos/Dropdown/index.js b/fir_admin/src/views/appinfos/Dropdown/index.js deleted file mode 100644 index bc0c171..0000000 --- a/fir_admin/src/views/appinfos/Dropdown/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { default as CommentDropdown } from './Comment' -export { default as PlatformDropdown } from './Platform' -export { default as SourceUrlDropdown } from './SourceUrl' diff --git a/fir_admin/src/views/appinfos/list.vue b/fir_admin/src/views/appinfos/list.vue index 316b094..6280218 100644 --- a/fir_admin/src/views/appinfos/list.vue +++ b/fir_admin/src/views/appinfos/list.vue @@ -196,7 +196,6 @@ export default { }, methods: { deleteApp(app_id) { - this.$confirm('此操作将永久删除该应用, 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', @@ -212,8 +211,8 @@ export default { this.$message({ type: 'info', message: '已取消删除' - }); - }); + }) + }) }, handleFilter() { this.listQuery.page = 1 diff --git a/fir_admin/src/views/authentication/Detail.vue b/fir_admin/src/views/authentication/Detail.vue new file mode 100644 index 0000000..efd01e9 --- /dev/null +++ b/fir_admin/src/views/authentication/Detail.vue @@ -0,0 +1,140 @@ + + + + diff --git a/fir_admin/src/views/authentication/list.vue b/fir_admin/src/views/authentication/list.vue new file mode 100644 index 0000000..fd90edc --- /dev/null +++ b/fir_admin/src/views/authentication/list.vue @@ -0,0 +1,199 @@ + + + diff --git a/fir_admin/src/views/storage/Detail.vue b/fir_admin/src/views/storage/Detail.vue new file mode 100644 index 0000000..efd01e9 --- /dev/null +++ b/fir_admin/src/views/storage/Detail.vue @@ -0,0 +1,140 @@ + + + + diff --git a/fir_admin/src/views/storage/list.vue b/fir_admin/src/views/storage/list.vue new file mode 100644 index 0000000..16986c3 --- /dev/null +++ b/fir_admin/src/views/storage/list.vue @@ -0,0 +1,197 @@ + + + diff --git a/fir_admin/src/views/userinfos/UserDetail.vue b/fir_admin/src/views/userinfos/UserDetail.vue index 3200bab..12d1e1b 100644 --- a/fir_admin/src/views/userinfos/UserDetail.vue +++ b/fir_admin/src/views/userinfos/UserDetail.vue @@ -56,6 +56,11 @@ + + + 审核认证信息 + + 用户需要先提交认证信息,才可以进行认证修改 diff --git a/fir_ser/admin/urls.py b/fir_ser/admin/urls.py index 6cfab6e..32625c4 100644 --- a/fir_ser/admin/urls.py +++ b/fir_ser/admin/urls.py @@ -15,8 +15,9 @@ Including another URLconf """ from django.urls import re_path from admin.views.login import LoginView, LoginUserView -from admin.views.user import UserInfoView +from admin.views.user import UserInfoView, UserCertificationInfoView from admin.views.app import AppInfoView, AppReleaseInfoView +from admin.views.storage import StorageInfoView urlpatterns = [ # path("",include(router.urls)), @@ -27,5 +28,7 @@ urlpatterns = [ re_path("^userinfo", UserInfoView.as_view()), re_path("^app/info", AppInfoView.as_view()), re_path("^app/release/info", AppReleaseInfoView.as_view()), + re_path("^certification/info", UserCertificationInfoView.as_view()), + re_path("^storage/info", StorageInfoView.as_view()), ] diff --git a/fir_ser/admin/views/storage.py b/fir_ser/admin/views/storage.py new file mode 100644 index 0000000..0862163 --- /dev/null +++ b/fir_ser/admin/views/storage.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- +# project: 4月 +# author: liuyu +# date: 2021/4/11 + +from django.contrib import auth +from api.models import Token, UserInfo, AppStorage +from rest_framework.response import Response +from api.utils.auth import AdminTokenAuthentication +from api.utils.serializer import AdminStorageSerializer, AdminUserCertificationSerializer +from django.core.cache import cache +from rest_framework.views import APIView +import binascii +import os, datetime +from api.utils.utils import get_captcha, valid_captcha, get_choices_dict +from api.utils.response import BaseResponse +from fir_ser.settings import CACHE_KEY_TEMPLATE, LOGIN +from api.utils.storage.caches import login_auth_failed +import logging +from api.utils.throttle import VisitRegister1Throttle, VisitRegister2Throttle +from rest_framework.pagination import PageNumberPagination + +logger = logging.getLogger(__name__) + + +class AppsPageNumber(PageNumberPagination): + page_size = 20 # 每页显示多少条 + page_size_query_param = 'limit' # URL中每页显示条数的参数 + page_query_param = 'page' # URL中页码的参数 + max_page_size = None # 最大页码数限制 + + +class StorageInfoView(APIView): + authentication_classes = [AdminTokenAuthentication, ] + + def get(self, request): + res = BaseResponse() + filter_data = {} + filter_fileds = ["id", "user_id", "name", "storage_type", "access_key", "bucket_name"] + for filed in filter_fileds: + f_value = request.query_params.get(filed, None) + if f_value: + filter_data[filed] = f_value + sort = request.query_params.get("sort", "-created_time") + page_obj = AppsPageNumber() + obj_list = AppStorage.objects.filter(**filter_data).order_by(sort) + page_serializer = page_obj.paginate_queryset(queryset=obj_list, request=request, + view=self) + serializer = AdminStorageSerializer(page_serializer, many=True) + res.data = serializer.data + res.total = obj_list.count() + return Response(res.dict) + + def put(self, request): + res = BaseResponse() + data = request.data + pk = data.get("id", None) + if not pk: + res.code = 1003 + res.msg = "参数错误" + return Response(res.dict) + user_obj = UserInfo.objects.filter(pk=pk).first() + if user_obj: + data['pk'] = pk + users_serializer = AdminStorageSerializer(user_obj, data=data, partial=True) + if users_serializer.is_valid(): + users_serializer.save() + res.data = users_serializer.data + return Response(res.dict) + res.code = 1004 + res.msg = "数据校验失败" + return Response(res.dict) diff --git a/fir_ser/admin/views/user.py b/fir_ser/admin/views/user.py index 0d53a51..e3b702d 100644 --- a/fir_ser/admin/views/user.py +++ b/fir_ser/admin/views/user.py @@ -8,7 +8,7 @@ from django.contrib import auth from api.models import Token, UserInfo, UserCertificationInfo from rest_framework.response import Response from api.utils.auth import AdminTokenAuthentication -from api.utils.serializer import AdminUserInfoSerializer +from api.utils.serializer import AdminUserInfoSerializer, AdminUserCertificationSerializer from django.core.cache import cache from rest_framework.views import APIView import binascii @@ -80,3 +80,46 @@ class UserInfoView(APIView): res.code = 1004 res.msg = "数据校验失败" return Response(res.dict) + + +class UserCertificationInfoView(APIView): + authentication_classes = [AdminTokenAuthentication, ] + + def get(self, request): + res = BaseResponse() + filter_data = {} + filter_fileds = ["id", "card", "name", "status"] + for filed in filter_fileds: + f_value = request.query_params.get(filed, None) + if f_value: + filter_data[filed] = f_value + sort = request.query_params.get("sort", "-created_time") + page_obj = AppsPageNumber() + obj_list = UserCertificationInfo.objects.filter(**filter_data).order_by(sort) + page_serializer = page_obj.paginate_queryset(queryset=obj_list, request=request, + view=self) + users_serializer = AdminUserCertificationSerializer(page_serializer, many=True) + res.data = users_serializer.data + res.total = obj_list.count() + return Response(res.dict) + + def put(self, request): + res = BaseResponse() + data = request.data + pk = data.get("id", None) + if not pk: + res.code = 1003 + res.msg = "参数错误" + return Response(res.dict) + obj = UserCertificationInfo.objects.filter(id=pk).first() + if obj: + data['pk'] = pk + users_serializer = AdminUserCertificationSerializer(obj, data=data, partial=True) + if users_serializer.is_valid(): + users_serializer.save() + res.data = users_serializer.data + return Response(res.dict) + print(users_serializer.errors) + res.code = 1004 + res.msg = "数据校验失败" + return Response(res.dict) diff --git a/fir_ser/api/models.py b/fir_ser/api/models.py index c8bc069..bc7ceb0 100644 --- a/fir_ser/api/models.py +++ b/fir_ser/api/models.py @@ -404,10 +404,10 @@ class UserCertificationInfo(models.Model): related_name='certification') name = 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) 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="备注") created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") reviewed_time = models.DateTimeField(auto_now_add=False, verbose_name="审核时间") diff --git a/fir_ser/api/utils/serializer.py b/fir_ser/api/utils/serializer.py index cec12cd..1397a39 100644 --- a/fir_ser/api/utils/serializer.py +++ b/fir_ser/api/utils/serializer.py @@ -4,7 +4,7 @@ from api.utils.app.apputils import bytes2human from api.utils.TokenManager import DownloadToken from api.utils.app.supersignutils import get_redirect_server_domain from api.utils.storage.storage import Storage -from api.utils.utils import get_developer_udided, get_choices_dict +from api.utils.utils import get_developer_udided, get_choices_dict, get_choices_name_from_key from api.utils.storage.caches import get_user_free_download_times, get_user_cert_auth_status import os, json, logging @@ -20,7 +20,7 @@ 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": storage = self.context.get("storage", None) else: - if isinstance(obj, models.Apps): + if isinstance(obj, models.Apps) or isinstance(obj, models.UserCertificationInfo): storage = Storage(obj.user_id) elif isinstance(obj, models.AppReleaseInfo): storage = Storage(obj.app_id.user_id) @@ -83,7 +83,7 @@ class AdminUserInfoSerializer(UserInfoSerializer): read_only_fields = ["id", "head_img", "free_download_times", "last_login", "is_superuser", "last_name", "is_staff", "uid", "storage_active", "supersign_active", "date_joined", "download_times", "all_download_times", "storage", "groups", - "user_permissions"] + "user_permissions", "certification_id"] gender_choices = serializers.SerializerMethodField() @@ -105,6 +105,11 @@ class AdminUserInfoSerializer(UserInfoSerializer): def get_certification_status_choices(self, obj): return get_choices_dict(models.UserCertificationInfo.status_choices) + certification_id = serializers.SerializerMethodField() + + def get_certification_id(self, obj): + return models.UserCertificationInfo.objects.filter(user_id=obj).values('id').first() + def update(self, instance, validated_data): return super(AdminUserInfoSerializer, self).update(instance, validated_data) @@ -322,7 +327,6 @@ class AdminAppReleaseSerializer(AppReleaseSerializer): return get_choices_dict(obj.release_choices) 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: @@ -357,6 +361,13 @@ class StorageSerializer(serializers.ModelSerializer): return None +class AdminStorageSerializer(StorageSerializer): + class Meta: + model = models.AppStorage + fields = "__all__" + read_only_fields = ["id", "user_id", "updated_time", "created_time"] + + class DeveloperSerializer(serializers.ModelSerializer): class Meta: model = models.AppIOSDeveloperInfo @@ -447,3 +458,29 @@ class UserCertificationSerializer(serializers.ModelSerializer): class Meta: model = models.UserCertificationInfo exclude = ["id", "user_id", "created_time"] + + +class AdminUserCertificationSerializer(serializers.ModelSerializer): + class Meta: + model = models.UserCertificationInfo + fields = "__all__" + read_only_fields = ["id", "user_id", "reviewed_time", "created_time"] + + certification_status_choices = serializers.SerializerMethodField() + + def get_certification_status_choices(self, obj): + return get_choices_dict(obj.status_choices) + + certification_infos = serializers.SerializerMethodField() + + def get_certification_infos(self, obj): + result = [] + for c_info in models.CertificationInfo.objects.filter(user_id=obj.user_id).all(): + result.append({ + 'name': get_choices_name_from_key(models.CertificationInfo.type_choices, c_info.type), + 'certification_url': get_download_url_from_context(self, obj, '', c_info.certification_url) + }) + return result + + def update(self, instance, validated_data): + return super(AdminUserCertificationSerializer, self).update(instance, validated_data) diff --git a/fir_ser/api/utils/utils.py b/fir_ser/api/utils/utils.py index 3d9806b..35a3590 100644 --- a/fir_ser/api/utils/utils.py +++ b/fir_ser/api/utils/utils.py @@ -304,3 +304,11 @@ def get_choices_dict(choices): for choice in choices_org_list: result.append({'id': choice[0], 'name': choice[1]}) return result + + +def get_choices_name_from_key(choices, key): + choices_org_list = list(choices) + for choice in choices_org_list: + if choice[0] == key: + return choice[1] + return ''