Files
chunyu_project/user/views/settings.py
T
2026-08-05 23:59:15 +08:00

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