From 3a8df43de7b99d553b5112316d7f2e1c033b9953 Mon Sep 17 00:00:00 2001 From: zyc <1439655764@qq.com> Date: Thu, 29 Jan 2026 10:41:26 +0800 Subject: [PATCH] fix bug --- apps/inventory/dashboard_views.py | 47 ++++++++++++++++ apps/inventory/urls.py | 3 + apps/users/urls.py | 16 +++++- apps/users/views.py | 94 ++++++++++++++++++++++++++++++- config/urls.py | 3 + 5 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 apps/inventory/dashboard_views.py diff --git a/apps/inventory/dashboard_views.py b/apps/inventory/dashboard_views.py new file mode 100644 index 0000000..93a32ff --- /dev/null +++ b/apps/inventory/dashboard_views.py @@ -0,0 +1,47 @@ +""" +仪表盘视图 - 统计数据 +""" +from rest_framework import viewsets +from rest_framework.decorators import action + +from utils.response import success +from apps.admins.authentication import AdminJWTAuthentication +from apps.admins.permissions import IsAdminUser +from apps.users.models import User +from apps.devices.models import DeviceType, DeviceBatch, Device + + +class DashboardViewSet(viewsets.ViewSet): + """仪表盘统计视图集""" + + authentication_classes = [AdminJWTAuthentication] + permission_classes = [IsAdminUser] + + @action(detail=False, methods=['get']) + def stats(self, request): + """ + 获取仪表盘统计数据 + GET /api/admin/dashboard/stats + """ + # 用户总数 + user_count = User.objects.count() + + # 设备类型数 + device_type_count = DeviceType.objects.count() + + # 批次数 + batch_count = DeviceBatch.objects.count() + + # 设备统计 + device_count = Device.objects.count() + bound_device_count = Device.objects.filter(status='bound').count() + in_stock_device_count = Device.objects.filter(status='in_stock').count() + + return success(data={ + 'user_count': user_count, + 'device_count': device_count, + 'device_type_count': device_type_count, + 'batch_count': batch_count, + 'bound_device_count': bound_device_count, + 'in_stock_device_count': in_stock_device_count, + }) diff --git a/apps/inventory/urls.py b/apps/inventory/urls.py index 394f009..c9a62b2 100644 --- a/apps/inventory/urls.py +++ b/apps/inventory/urls.py @@ -4,13 +4,16 @@ from django.urls import path, include from rest_framework.routers import DefaultRouter from .views import DeviceTypeViewSet, DeviceBatchViewSet +from .dashboard_views import DashboardViewSet admin_router = DefaultRouter() admin_router.register('device-types', DeviceTypeViewSet, basename='admin-device-types') admin_router.register('device-batches', DeviceBatchViewSet, basename='admin-device-batches') +admin_router.register('dashboard', DashboardViewSet, basename='admin-dashboard') urlpatterns = [] admin_urlpatterns = [ path('', include(admin_router.urls)), ] + diff --git a/apps/users/urls.py b/apps/users/urls.py index 30d402d..df3a7a1 100644 --- a/apps/users/urls.py +++ b/apps/users/urls.py @@ -1,10 +1,13 @@ """ -用户模块URL配置 - 仅App端接口 +用户模块URL配置 +- App端接口:/api/v1/auth/*, /api/v1/users/* +- 管理端接口:/api/admin/users/* """ from django.urls import path, include from rest_framework.routers import DefaultRouter -from .views import AuthViewSet, UserViewSet +from .views import AuthViewSet, UserViewSet, AdminUserManageViewSet +# App端路由 router = DefaultRouter() router.register('auth', AuthViewSet, basename='auth') router.register('users', UserViewSet, basename='users') @@ -12,3 +15,12 @@ router.register('users', UserViewSet, basename='users') urlpatterns = [ path('', include(router.urls)), ] + +# 管理端路由 +admin_router = DefaultRouter() +admin_router.register('users', AdminUserManageViewSet, basename='admin-users') + +admin_urlpatterns = [ + path('', include(admin_router.urls)), +] + diff --git a/apps/users/views.py b/apps/users/views.py index 7edf932..528c1a5 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -5,9 +5,11 @@ from rest_framework import viewsets, status from rest_framework.decorators import action from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework_simplejwt.tokens import RefreshToken +from django.db.models import Count from utils.response import success, error -from apps.admins.authentication import AppJWTAuthentication +from apps.admins.authentication import AppJWTAuthentication, AdminJWTAuthentication +from apps.admins.permissions import IsAdminUser from .models import User from .serializers import ( UserSerializer, @@ -118,3 +120,93 @@ class UserViewSet(viewsets.ViewSet): serializer.save() return success(data=UserSerializer(request.user).data, message='更新成功') return error(message=str(serializer.errors)) + + +class AdminUserManageViewSet(viewsets.ViewSet): + """App用户管理视图集 - 管理端""" + + authentication_classes = [AdminJWTAuthentication] + permission_classes = [IsAdminUser] + + def list(self, request): + """ + 用户列表 + GET /api/admin/users + """ + queryset = User.objects.all().order_by('-created_at') + + # 搜索条件 + phone = request.query_params.get('phone') + nickname = request.query_params.get('nickname') + is_active = request.query_params.get('is_active') + + if phone: + queryset = queryset.filter(phone__contains=phone) + if nickname: + queryset = queryset.filter(nickname__contains=nickname) + if is_active is not None: + queryset = queryset.filter(is_active=(is_active.lower() == 'true')) + + # 标注统计数据 + queryset = queryset.annotate( + spirit_count=Count('spirits', distinct=True), + device_count=Count('user_devices', distinct=True) + ) + + # 分页 + page = int(request.query_params.get('page', 1)) + page_size = int(request.query_params.get('page_size', 10)) + start = (page - 1) * page_size + end = start + page_size + + total = queryset.count() + users = queryset[start:end] + + # 手动构造带统计的数据 + items = [] + for user in users: + data = UserSerializer(user).data + data['spirit_count'] = getattr(user, 'spirit_count', 0) + data['device_count'] = getattr(user, 'device_count', 0) + items.append(data) + + return success(data={ + 'total': total, + 'items': items + }) + + def retrieve(self, request, pk=None): + """ + 用户详情 + GET /api/admin/users/{id} + """ + try: + user = User.objects.get(pk=pk) + except User.DoesNotExist: + return error(message='用户不存在') + + data = UserDetailSerializer(user).data + data['spirit_count'] = user.spirits.count() + data['device_count'] = user.user_devices.count() + + return success(data=data) + + @action(detail=True, methods=['post'], url_path='toggle-status') + def toggle_status(self, request, pk=None): + """ + 启用/禁用用户 + POST /api/admin/users/{id}/toggle-status + """ + try: + user = User.objects.get(pk=pk) + except User.DoesNotExist: + return error(message='用户不存在') + + user.is_active = not user.is_active + user.save() + + return success( + data=UserSerializer(user).data, + message='已启用' if user.is_active else '已禁用' + ) + diff --git a/config/urls.py b/config/urls.py index 9be7bb4..b4ccd3b 100644 --- a/config/urls.py +++ b/config/urls.py @@ -18,12 +18,15 @@ app_api_patterns = [ # ============ Web管理端路由 (管理员,用户名密码登录) ============ from apps.inventory.urls import admin_urlpatterns as inventory_admin_urls +from apps.users.urls import admin_urlpatterns as users_admin_urls admin_api_patterns = [ # 管理员认证和个人信息 path('', include('apps.admins.urls')), # 业务管理接口 path('', include(inventory_admin_urls)), + # App用户管理 + path('', include(users_admin_urls)), ] urlpatterns = [