fix bug
This commit is contained in:
parent
c0deacd79c
commit
3a8df43de7
47
apps/inventory/dashboard_views.py
Normal file
47
apps/inventory/dashboard_views.py
Normal 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,
|
||||||
|
})
|
||||||
@ -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)),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -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)),
|
||||||
|
]
|
||||||
|
|
||||||
|
|||||||
@ -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 '已禁用'
|
||||||
|
)
|
||||||
|
|
||||||
|
|||||||
@ -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 = [
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user