261 lines
10 KiB
Python
261 lines
10 KiB
Python
import json
|
|
from rest_framework.views import APIView
|
|
from rest_framework.permissions import IsAuthenticated
|
|
from rest_framework.parsers import MultiPartParser, FormParser
|
|
from drf_yasg.utils import swagger_auto_schema
|
|
from drf_yasg import openapi
|
|
from user.models import UserDevice
|
|
|
|
from utils.response_codes import create_standardized_response, create_standardized_error_response
|
|
|
|
success_response = openapi.Response('成功', examples={'application/json': {'code': 10000, 'message': 'success'}})
|
|
unauthorized_response = openapi.Response('未授权', examples={'application/json': {'code': 40101, 'message': '未登录'}})
|
|
|
|
|
|
class UploadCoverAPIView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
parser_classes = [MultiPartParser, FormParser]
|
|
|
|
@swagger_auto_schema(
|
|
tags=['用户'],
|
|
operation_summary='上传个人主页封面',
|
|
operation_description='上传个人主页封面图片,支持jpg/png/jpeg格式,最大5MB',
|
|
manual_parameters=[
|
|
openapi.Parameter('cover', openapi.IN_FORM, description='封面图片文件', type=openapi.TYPE_FILE, required=True),
|
|
],
|
|
responses={200: success_response, 400: '参数错误', 401: unauthorized_response},
|
|
)
|
|
def post(self, request):
|
|
cover_file = request.FILES.get('cover')
|
|
if not cover_file:
|
|
return create_standardized_error_response(
|
|
code=40001,
|
|
message='请选择要上传的封面图片'
|
|
)
|
|
|
|
if cover_file.size > 5 * 1024 * 1024:
|
|
return create_standardized_error_response(
|
|
code=40002,
|
|
message='图片大小不能超过5MB'
|
|
)
|
|
|
|
allowed_types = ['image/jpeg', 'image/png', 'image/jpg', 'image/webp']
|
|
if cover_file.content_type not in allowed_types:
|
|
return create_standardized_error_response(
|
|
code=40003,
|
|
message='仅支持 jpg/png/webp 格式的图片'
|
|
)
|
|
|
|
user = request.user
|
|
if user.cover_image:
|
|
try:
|
|
user.cover_image.delete(save=False)
|
|
except Exception:
|
|
pass
|
|
|
|
user.cover_image = cover_file
|
|
user.save(update_fields=['cover_image'])
|
|
|
|
return create_standardized_response(
|
|
data={
|
|
'cover_image': user.cover_image.url if user.cover_image else '',
|
|
'message': '封面上传成功',
|
|
}
|
|
)
|
|
|
|
|
|
class PrivacySettingsAPIView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
@swagger_auto_schema(
|
|
tags=['用户'],
|
|
operation_summary='获取隐私设置',
|
|
operation_description='获取当前用户的隐私设置',
|
|
responses={200: success_response, 401: unauthorized_response},
|
|
)
|
|
def get(self, request):
|
|
user = request.user
|
|
return create_standardized_response(
|
|
data={
|
|
'privacy_profile': user.privacy_profile,
|
|
'privacy_articles': user.privacy_articles,
|
|
'privacy_friends': user.privacy_friends,
|
|
}
|
|
)
|
|
|
|
@swagger_auto_schema(
|
|
tags=['用户'],
|
|
operation_summary='更新隐私设置',
|
|
operation_description='更新当前用户的隐私设置',
|
|
request_body=openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
properties={
|
|
'privacy_profile': openapi.Schema(type=openapi.TYPE_STRING, description='主页可见性', enum=['public', 'friends_only', 'private']),
|
|
'privacy_articles': openapi.Schema(type=openapi.TYPE_STRING, description='文章可见性', enum=['public', 'friends_only', 'private']),
|
|
'privacy_friends': openapi.Schema(type=openapi.TYPE_STRING, description='好友列表可见性', enum=['public', 'friends_only', 'private']),
|
|
},
|
|
),
|
|
responses={200: success_response, 400: '参数错误', 401: unauthorized_response},
|
|
)
|
|
def put(self, request):
|
|
user = request.user
|
|
valid_choices = ['public', 'friends_only', 'private']
|
|
|
|
fields = ['privacy_profile', 'privacy_articles', 'privacy_friends']
|
|
for field in fields:
|
|
value = request.data.get(field)
|
|
if value is not None:
|
|
if value not in valid_choices:
|
|
return create_standardized_error_response(
|
|
code=40001,
|
|
message=f'{field} 的值不合法'
|
|
)
|
|
setattr(user, field, value)
|
|
|
|
user.save(update_fields=fields)
|
|
return create_standardized_response(
|
|
data={
|
|
'privacy_profile': user.privacy_profile,
|
|
'privacy_articles': user.privacy_articles,
|
|
'privacy_friends': user.privacy_friends,
|
|
'message': '隐私设置已更新',
|
|
}
|
|
)
|
|
|
|
|
|
class NotificationSettingsAPIView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
@swagger_auto_schema(
|
|
tags=['用户'],
|
|
operation_summary='获取消息通知设置',
|
|
operation_description='获取当前用户的消息通知偏好设置',
|
|
responses={200: success_response, 401: unauthorized_response},
|
|
)
|
|
def get(self, request):
|
|
user = request.user
|
|
return create_standardized_response(
|
|
data={
|
|
'notify_email': user.notify_email,
|
|
'notify_browser': user.notify_browser,
|
|
'notify_reply': user.notify_reply,
|
|
'notify_like': user.notify_like,
|
|
'notify_follow': user.notify_follow,
|
|
'notify_system': user.notify_system,
|
|
}
|
|
)
|
|
|
|
@swagger_auto_schema(
|
|
tags=['用户'],
|
|
operation_summary='更新消息通知设置',
|
|
operation_description='更新当前用户的消息通知偏好设置',
|
|
request_body=openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
properties={
|
|
'notify_email': openapi.Schema(type=openapi.TYPE_BOOLEAN, description='邮件通知'),
|
|
'notify_browser': openapi.Schema(type=openapi.TYPE_BOOLEAN, description='浏览器通知'),
|
|
'notify_reply': openapi.Schema(type=openapi.TYPE_BOOLEAN, description='回复通知'),
|
|
'notify_like': openapi.Schema(type=openapi.TYPE_BOOLEAN, description='点赞通知'),
|
|
'notify_follow': openapi.Schema(type=openapi.TYPE_BOOLEAN, description='关注通知'),
|
|
'notify_system': openapi.Schema(type=openapi.TYPE_BOOLEAN, description='系统通知'),
|
|
},
|
|
),
|
|
responses={200: success_response, 401: unauthorized_response},
|
|
)
|
|
def put(self, request):
|
|
user = request.user
|
|
|
|
fields = ['notify_email', 'notify_browser', 'notify_reply', 'notify_like', 'notify_follow', 'notify_system']
|
|
for field in fields:
|
|
value = request.data.get(field)
|
|
if value is not None:
|
|
if not isinstance(value, bool):
|
|
value = str(value).lower() in ('true', '1', 'yes')
|
|
setattr(user, field, value)
|
|
|
|
user.save(update_fields=fields)
|
|
return create_standardized_response(
|
|
data={
|
|
'notify_email': user.notify_email,
|
|
'notify_browser': user.notify_browser,
|
|
'notify_reply': user.notify_reply,
|
|
'notify_like': user.notify_like,
|
|
'notify_follow': user.notify_follow,
|
|
'notify_system': user.notify_system,
|
|
'message': '通知设置已更新',
|
|
}
|
|
)
|
|
|
|
|
|
class UserDeviceListView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
@swagger_auto_schema(
|
|
tags=['用户'],
|
|
operation_summary='获取登录设备列表',
|
|
operation_description='获取当前用户的所有登录设备列表',
|
|
responses={200: success_response, 401: unauthorized_response},
|
|
)
|
|
def get(self, request):
|
|
user = request.user
|
|
devices = UserDevice.objects.filter(user=user).order_by('-last_active')
|
|
data = []
|
|
for d in devices:
|
|
data.append({
|
|
'id': d.id,
|
|
'device_name': d.device_name,
|
|
'device_type': d.device_type,
|
|
'browser': d.browser,
|
|
'os': d.os,
|
|
'ip_address': str(d.ip_address) if d.ip_address else '',
|
|
'location': d.location,
|
|
'last_active': d.last_active.strftime('%Y-%m-%d %H:%M:%S'),
|
|
'created_at': d.created_at.strftime('%Y-%m-%d %H:%M:%S'),
|
|
'is_current': d.is_current,
|
|
})
|
|
return create_standardized_response(data={'devices': data, 'total': len(data)})
|
|
|
|
|
|
class UserDeviceRemoveView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
@swagger_auto_schema(
|
|
tags=['用户'],
|
|
operation_summary='移除登录设备',
|
|
operation_description='移除指定的登录设备(远程登出)',
|
|
manual_parameters=[
|
|
openapi.Parameter('pk', openapi.IN_PATH, description='设备ID', type=openapi.TYPE_INTEGER, required=True),
|
|
],
|
|
responses={200: success_response, 404: '设备不存在', 401: unauthorized_response},
|
|
)
|
|
def delete(self, request, pk):
|
|
user = request.user
|
|
device = UserDevice.objects.filter(user=user, pk=pk).first()
|
|
if not device:
|
|
return create_standardized_error_response(
|
|
code=40401,
|
|
message='设备不存在'
|
|
)
|
|
if device.is_current:
|
|
return create_standardized_error_response(
|
|
code=40001,
|
|
message='不能移除当前设备'
|
|
)
|
|
device.delete()
|
|
return create_standardized_response(data={'message': '设备已移除'})
|
|
|
|
|
|
class UserDeviceClearOthersView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
@swagger_auto_schema(
|
|
tags=['用户'],
|
|
operation_summary='清除其他设备',
|
|
operation_description='移除除当前设备外的所有其他登录设备',
|
|
responses={200: success_response, 401: unauthorized_response},
|
|
)
|
|
def post(self, request):
|
|
user = request.user
|
|
count, _ = UserDevice.objects.filter(user=user).exclude(is_current=True).delete()
|
|
return create_standardized_response(data={'message': f'已移除 {count} 个设备', 'removed_count': count})
|