This commit is contained in:
zyc 2026-01-29 10:41:26 +08:00
parent c0deacd79c
commit 3a8df43de7
5 changed files with 160 additions and 3 deletions

View File

@ -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,
})

View File

@ -4,13 +4,16 @@
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from .views import DeviceTypeViewSet, DeviceBatchViewSet from .views import DeviceTypeViewSet, DeviceBatchViewSet
from .dashboard_views import DashboardViewSet
admin_router = DefaultRouter() admin_router = DefaultRouter()
admin_router.register('device-types', DeviceTypeViewSet, basename='admin-device-types') admin_router.register('device-types', DeviceTypeViewSet, basename='admin-device-types')
admin_router.register('device-batches', DeviceBatchViewSet, basename='admin-device-batches') admin_router.register('device-batches', DeviceBatchViewSet, basename='admin-device-batches')
admin_router.register('dashboard', DashboardViewSet, basename='admin-dashboard')
urlpatterns = [] urlpatterns = []
admin_urlpatterns = [ admin_urlpatterns = [
path('', include(admin_router.urls)), path('', include(admin_router.urls)),
] ]

View File

@ -1,10 +1,13 @@
""" """
用户模块URL配置 - 仅App端接口 用户模块URL配置
- App端接口/api/v1/auth/*, /api/v1/users/*
- 管理端接口/api/admin/users/*
""" """
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from .views import AuthViewSet, UserViewSet from .views import AuthViewSet, UserViewSet, AdminUserManageViewSet
# App端路由
router = DefaultRouter() router = DefaultRouter()
router.register('auth', AuthViewSet, basename='auth') router.register('auth', AuthViewSet, basename='auth')
router.register('users', UserViewSet, basename='users') router.register('users', UserViewSet, basename='users')
@ -12,3 +15,12 @@ router.register('users', UserViewSet, basename='users')
urlpatterns = [ urlpatterns = [
path('', include(router.urls)), path('', include(router.urls)),
] ]
# 管理端路由
admin_router = DefaultRouter()
admin_router.register('users', AdminUserManageViewSet, basename='admin-users')
admin_urlpatterns = [
path('', include(admin_router.urls)),
]

View File

@ -5,9 +5,11 @@ from rest_framework import viewsets, status
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework_simplejwt.tokens import RefreshToken from rest_framework_simplejwt.tokens import RefreshToken
from django.db.models import Count
from utils.response import success, error 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 .models import User
from .serializers import ( from .serializers import (
UserSerializer, UserSerializer,
@ -118,3 +120,93 @@ class UserViewSet(viewsets.ViewSet):
serializer.save() serializer.save()
return success(data=UserSerializer(request.user).data, message='更新成功') return success(data=UserSerializer(request.user).data, message='更新成功')
return error(message=str(serializer.errors)) 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 '已禁用'
)

View File

@ -18,12 +18,15 @@ app_api_patterns = [
# ============ Web管理端路由 (管理员,用户名密码登录) ============ # ============ Web管理端路由 (管理员,用户名密码登录) ============
from apps.inventory.urls import admin_urlpatterns as inventory_admin_urls 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 = [ admin_api_patterns = [
# 管理员认证和个人信息 # 管理员认证和个人信息
path('', include('apps.admins.urls')), path('', include('apps.admins.urls')),
# 业务管理接口 # 业务管理接口
path('', include(inventory_admin_urls)), path('', include(inventory_admin_urls)),
# App用户管理
path('', include(users_admin_urls)),
] ]
urlpatterns = [ urlpatterns = [