861 lines
31 KiB
Python
861 lines
31 KiB
Python
# apps/accounts/views.py
|
||
from django.contrib.auth import authenticate, login, logout
|
||
from django.core.exceptions import ValidationError
|
||
from django.contrib.auth.hashers import check_password
|
||
from django.utils.decorators import method_decorator
|
||
from django.views.decorators.csrf import csrf_exempt
|
||
from rest_framework.views import APIView
|
||
from rest_framework.decorators import api_view, permission_classes
|
||
from rest_framework.response import Response
|
||
from rest_framework import status
|
||
from rest_framework.permissions import AllowAny, IsAuthenticated, IsAdminUser
|
||
from rest_framework.authtoken.models import Token
|
||
from django.db.models import Q
|
||
from django.shortcuts import get_object_or_404
|
||
import uuid
|
||
import logging
|
||
import traceback
|
||
from apps.accounts.models import User, UserGoal
|
||
from apps.accounts.services.auth_service import (
|
||
authenticate_user, create_user, generate_token, delete_token
|
||
)
|
||
from apps.accounts.services.utils import (
|
||
convert_to_uuid, format_user_response, validate_uuid_param
|
||
)
|
||
from apps.accounts.services.goal_service import (
|
||
generate_recommended_reply, get_active_goal, get_conversation_summary,
|
||
get_last_message
|
||
)
|
||
from .serializers import UserGoalSerializer
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
@method_decorator(csrf_exempt, name='dispatch')
|
||
class LoginView(APIView):
|
||
"""用户登录视图"""
|
||
authentication_classes = [] # 清空认证类
|
||
permission_classes = [AllowAny]
|
||
|
||
def post(self, request):
|
||
try:
|
||
username = request.data.get('username')
|
||
password = request.data.get('password')
|
||
|
||
# 参数验证
|
||
if not username or not password:
|
||
return Response({
|
||
"code": 400,
|
||
"message": "请提供用户名和密码",
|
||
"data": None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 验证用户
|
||
user = authenticate_user(request, username, password)
|
||
|
||
if user is not None:
|
||
# 获取或创建token
|
||
token = generate_token(user)
|
||
|
||
# 登录用户(可选)
|
||
login(request, user)
|
||
|
||
return Response({
|
||
"code": 200,
|
||
"message": "登录成功",
|
||
"data": {
|
||
"id": str(user.id),
|
||
"username": user.username,
|
||
"email": user.email,
|
||
"name": user.name,
|
||
"role": user.role,
|
||
"department": user.department,
|
||
"group": user.group,
|
||
"token": token
|
||
}
|
||
})
|
||
else:
|
||
return Response({
|
||
"code": 401,
|
||
"message": "用户名或密码错误",
|
||
"data": None
|
||
}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
except Exception as e:
|
||
logger.error(f"登录失败: {str(e)}")
|
||
logger.error(f"错误类型: {type(e)}")
|
||
logger.error(f"错误堆栈: {traceback.format_exc()}")
|
||
|
||
return Response({
|
||
"code": 500,
|
||
"message": "登录失败,请稍后重试",
|
||
"data": None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
@method_decorator(csrf_exempt, name='dispatch')
|
||
class RegisterView(APIView):
|
||
"""用户注册视图"""
|
||
permission_classes = [AllowAny]
|
||
|
||
def post(self, request):
|
||
try:
|
||
data = request.data
|
||
|
||
# 检查必填字段
|
||
required_fields = ['username', 'password', 'email', 'role', 'name']
|
||
for field in required_fields:
|
||
if not data.get(field):
|
||
return Response({
|
||
"code": 400,
|
||
"message": f"缺少必填字段: {field}",
|
||
"data": None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 验证角色
|
||
valid_roles = ['admin', 'leader', 'member']
|
||
roles_str = ', '.join(valid_roles) # 先构造角色字符串
|
||
if data['role'] not in valid_roles:
|
||
return Response({
|
||
"code": 400,
|
||
"message": f"无效的角色,必须是: {roles_str}",
|
||
"data": None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 创建用户
|
||
user = create_user(data)
|
||
if isinstance(user, dict): # 错误响应
|
||
return Response(user, status=user.get('status', status.HTTP_400_BAD_REQUEST))
|
||
|
||
# 生成认证令牌
|
||
token = generate_token(user)
|
||
|
||
return Response({
|
||
"code": 200,
|
||
"message": "注册成功",
|
||
"data": {
|
||
"id": str(user.id),
|
||
"username": user.username,
|
||
"email": user.email,
|
||
"role": user.role,
|
||
"department": user.department,
|
||
"name": user.name,
|
||
"group": user.group,
|
||
"token": token,
|
||
"created_at": user.date_joined.strftime('%Y-%m-%d %H:%M:%S')
|
||
}
|
||
}, status=status.HTTP_201_CREATED)
|
||
|
||
except Exception as e:
|
||
logger.error(f"注册失败: {str(e)}")
|
||
logger.error(f"错误类型: {type(e)}")
|
||
logger.error(f"错误堆栈: {traceback.format_exc()}")
|
||
return Response({
|
||
"code": 500,
|
||
"message": f"注册失败: {str(e)}",
|
||
"data": None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
@method_decorator(csrf_exempt, name='dispatch')
|
||
class LogoutView(APIView):
|
||
"""用户登出视图"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
try:
|
||
# 删除用户的token
|
||
delete_token(request.user)
|
||
# 执行django的登出
|
||
logout(request)
|
||
|
||
return Response({
|
||
"code": 200,
|
||
"message": "登出成功",
|
||
"data": None
|
||
})
|
||
except Exception as e:
|
||
logger.error(f"登出失败: {str(e)}")
|
||
logger.error(traceback.format_exc())
|
||
return Response({
|
||
"code": 500,
|
||
"message": f"登出失败: {str(e)}",
|
||
"data": None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
@api_view(['GET', 'PUT'])
|
||
@permission_classes([IsAuthenticated])
|
||
def user_profile(request):
|
||
"""
|
||
获取或更新当前登录用户信息
|
||
"""
|
||
try:
|
||
if request.method == 'GET':
|
||
user = request.user
|
||
if not user.is_authenticated:
|
||
return Response({
|
||
'code': 401,
|
||
'message': '用户未认证',
|
||
'data': None
|
||
}, status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
data = {
|
||
'id': str(user.id),
|
||
'username': user.username,
|
||
'email': user.email,
|
||
'name': user.name,
|
||
'role': user.role,
|
||
'department': user.department,
|
||
'group': user.group,
|
||
'date_joined': user.date_joined.strftime('%Y-%m-%d %H:%M:%S')
|
||
}
|
||
return Response({
|
||
'code': 200,
|
||
'message': '获取用户信息成功',
|
||
'data': data
|
||
})
|
||
|
||
elif request.method == 'PUT':
|
||
try:
|
||
if not request.data:
|
||
return Response({
|
||
'code': 400,
|
||
'message': '请求数据为空或格式错误',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
except Exception as data_error:
|
||
return Response({
|
||
'code': 400,
|
||
'message': f'请求数据格式错误: {str(data_error)}。请确保提交的是有效的JSON格式数据,属性名必须使用双引号。',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
user = request.user
|
||
allowed_fields = ['email', 'name', 'department', 'group']
|
||
updated_fields = []
|
||
|
||
for field in allowed_fields:
|
||
if field in request.data:
|
||
# 检查name字段是否重名
|
||
if field == 'name' and request.data['name'] != user.name:
|
||
# 检查是否有其他用户使用相同name
|
||
if User.objects.filter(name=request.data['name']).exclude(id=user.id).exists():
|
||
return Response({
|
||
'code': 400,
|
||
'message': '用户名称已存在',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
setattr(user, field, request.data[field])
|
||
updated_fields.append(field)
|
||
|
||
if updated_fields:
|
||
try:
|
||
user.save()
|
||
return Response({
|
||
'code': 200,
|
||
'message': f'用户信息更新成功,已更新字段: {", ".join(updated_fields)}',
|
||
'data': {
|
||
'id': str(user.id),
|
||
'username': user.username,
|
||
'email': user.email,
|
||
'name': user.name,
|
||
'role': user.role,
|
||
'department': user.department,
|
||
'group': user.group,
|
||
}
|
||
})
|
||
except Exception as save_error:
|
||
logger.error(f"保存用户数据失败: {str(save_error)}")
|
||
return Response({
|
||
'code': 500,
|
||
'message': f'更新用户信息失败: {str(save_error)}',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
else:
|
||
return Response({
|
||
'code': 400,
|
||
'message': '没有提供任何可更新的字段',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
else:
|
||
return Response({
|
||
'code': 405,
|
||
'message': f'不支持的请求方法: {request.method}',
|
||
'data': None
|
||
}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
|
||
except Exception as e:
|
||
logger.error(f"处理用户信息请求失败: {str(e)}")
|
||
logger.error(traceback.format_exc())
|
||
return Response({
|
||
'code': 500,
|
||
'message': f'处理请求失败: {str(e)}',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
@csrf_exempt
|
||
@api_view(['POST'])
|
||
@permission_classes([IsAuthenticated])
|
||
def change_password(request):
|
||
"""修改密码"""
|
||
try:
|
||
old_password = request.data.get('old_password')
|
||
new_password = request.data.get('new_password')
|
||
|
||
# 验证参数
|
||
if not old_password or not new_password:
|
||
return Response({
|
||
"code": 400,
|
||
"message": "请提供旧密码和新密码",
|
||
"data": None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 验证旧密码
|
||
user = request.user
|
||
if not user.check_password(old_password):
|
||
return Response({
|
||
"code": 400,
|
||
"message": "旧密码错误",
|
||
"data": None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 验证新密码长度
|
||
if len(new_password) < 8:
|
||
return Response({
|
||
"code": 400,
|
||
"message": "新密码长度必须至少为8位",
|
||
"data": None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 修改密码
|
||
user.set_password(new_password)
|
||
user.save()
|
||
|
||
# 更新token
|
||
delete_token(user)
|
||
token = generate_token(user)
|
||
|
||
return Response({
|
||
"code": 200,
|
||
"message": "密码修改成功",
|
||
"data": {
|
||
"token": token
|
||
}
|
||
})
|
||
|
||
except Exception as e:
|
||
logger.error(f"密码修改失败: {str(e)}")
|
||
logger.error(traceback.format_exc())
|
||
return Response({
|
||
"code": 500,
|
||
"message": f"密码修改失败: {str(e)}",
|
||
"data": None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
@api_view(['GET'])
|
||
@permission_classes([IsAuthenticated])
|
||
def user_detail(request, pk):
|
||
"""获取用户详情"""
|
||
try:
|
||
# 使用通用UUID验证工具函数
|
||
uuid_obj, error_response = validate_uuid_param(pk)
|
||
if error_response:
|
||
return error_response
|
||
|
||
user = get_object_or_404(User, pk=uuid_obj)
|
||
|
||
return Response({
|
||
"code": 200,
|
||
"message": "获取用户信息成功",
|
||
"data": format_user_response(user)
|
||
})
|
||
except Exception as e:
|
||
logger.error(f"获取用户信息失败: {str(e)}")
|
||
logger.error(traceback.format_exc())
|
||
return Response({
|
||
"code": 500,
|
||
"message": f"获取用户信息失败: {str(e)}",
|
||
"data": None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
@api_view(['PUT'])
|
||
@permission_classes([IsAuthenticated])
|
||
def user_update(request, pk):
|
||
"""
|
||
更新用户信息
|
||
- 管理员可以更新任何用户
|
||
- 普通用户只能更新自己的信息
|
||
"""
|
||
try:
|
||
try:
|
||
if not request.data:
|
||
return Response({
|
||
'code': 400,
|
||
'message': '请求数据为空或格式错误',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
except Exception as data_error:
|
||
return Response({
|
||
'code': 400,
|
||
'message': f'请求数据格式错误: {str(data_error)}。请确保提交的是有效的JSON格式数据,属性名必须使用双引号。',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 使用通用UUID验证工具函数
|
||
uuid_obj, error_response = validate_uuid_param(pk)
|
||
if error_response:
|
||
return error_response
|
||
|
||
try:
|
||
user = User.objects.get(pk=uuid_obj)
|
||
except User.DoesNotExist:
|
||
return Response({
|
||
'code': 404,
|
||
'message': '用户不存在',
|
||
'data': None
|
||
}, status=status.HTTP_404_NOT_FOUND)
|
||
|
||
# 验证权限:用户只能修改自己的信息,管理员可以修改任何人
|
||
is_admin = request.user.is_staff or request.user.role == 'admin'
|
||
is_self_update = str(request.user.id) == str(user.id)
|
||
|
||
if not (is_admin or is_self_update):
|
||
return Response({
|
||
'code': 403,
|
||
'message': '权限不足,您只能修改自己的信息',
|
||
'data': None
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
# 根据用户权限决定可以修改的字段
|
||
if is_admin:
|
||
allowed_fields = ['email', 'role', 'department', 'group', 'is_active', 'name']
|
||
else:
|
||
# 普通用户只能修改部分字段
|
||
allowed_fields = ['email', 'name']
|
||
|
||
updated_fields = []
|
||
|
||
for field in allowed_fields:
|
||
if field in request.data:
|
||
setattr(user, field, request.data[field])
|
||
updated_fields.append(field)
|
||
|
||
if not updated_fields:
|
||
return Response({
|
||
'code': 400,
|
||
'message': '没有提供任何可更新的字段',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
user.save()
|
||
|
||
return Response({
|
||
'code': 200,
|
||
'message': '用户信息更新成功',
|
||
'data': format_user_response(user, include_is_active=is_admin)
|
||
})
|
||
except Exception as e:
|
||
logger.error(f"更新用户信息失败: {str(e)}")
|
||
logger.error(traceback.format_exc())
|
||
return Response({
|
||
'code': 500,
|
||
'message': f'更新用户信息失败: {str(e)}',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
@api_view(['DELETE'])
|
||
@permission_classes([IsAdminUser])
|
||
def user_delete(request, pk):
|
||
"""删除用户"""
|
||
try:
|
||
# 使用通用UUID验证工具函数
|
||
uuid_obj, error_response = validate_uuid_param(pk)
|
||
if error_response:
|
||
return error_response
|
||
|
||
try:
|
||
user = User.objects.get(pk=uuid_obj)
|
||
except User.DoesNotExist:
|
||
return Response({
|
||
'code': 404,
|
||
'message': '用户不存在',
|
||
'data': None
|
||
}, status=status.HTTP_404_NOT_FOUND)
|
||
|
||
if user.is_superuser or user.role == 'admin':
|
||
return Response({
|
||
'code': 403,
|
||
'message': '不允许删除管理员账户',
|
||
'data': None
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
username = user.username
|
||
user.delete()
|
||
|
||
return Response({
|
||
'code': 200,
|
||
'message': f'用户 {username} 删除成功',
|
||
'data': None
|
||
})
|
||
except Exception as e:
|
||
logger.error(f"删除用户失败: {str(e)}")
|
||
logger.error(traceback.format_exc())
|
||
return Response({
|
||
'code': 500,
|
||
'message': f'删除用户失败: {str(e)}',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
@csrf_exempt
|
||
@api_view(['POST'])
|
||
@permission_classes([IsAuthenticated])
|
||
def verify_token(request):
|
||
"""验证令牌有效性"""
|
||
try:
|
||
return Response({
|
||
"code": 200,
|
||
"message": "令牌有效",
|
||
"data": {
|
||
"is_valid": True,
|
||
"user": format_user_response(request.user)
|
||
}
|
||
})
|
||
except Exception as e:
|
||
logger.error(f"验证令牌失败: {str(e)}")
|
||
logger.error(traceback.format_exc())
|
||
return Response({
|
||
"code": 500,
|
||
"message": f"验证失败: {str(e)}",
|
||
"data": None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
@api_view(['GET'])
|
||
@permission_classes([IsAuthenticated])
|
||
def user_list(request):
|
||
"""获取用户列表"""
|
||
try:
|
||
page = int(request.query_params.get('page', 1))
|
||
page_size = int(request.query_params.get('page_size', 20))
|
||
keyword = request.query_params.get('keyword', '')
|
||
|
||
user = request.user
|
||
base_query = User.objects.all()
|
||
|
||
if user.role == 'admin':
|
||
users_query = base_query
|
||
elif user.role == 'leader':
|
||
users_query = base_query.filter(department=user.department)
|
||
else:
|
||
users_query = base_query.filter(id=user.id)
|
||
|
||
if keyword:
|
||
users_query = users_query.filter(
|
||
Q(username__icontains=keyword) |
|
||
Q(email__icontains=keyword) |
|
||
Q(name__icontains=keyword) |
|
||
Q(department__icontains=keyword)
|
||
)
|
||
|
||
total = users_query.count()
|
||
start = (page - 1) * page_size
|
||
end = start + page_size
|
||
users = users_query[start:end]
|
||
|
||
user_data = []
|
||
for u in users:
|
||
user_info = format_user_response(u, include_is_active=True)
|
||
user_info['date_joined'] = u.date_joined.strftime('%Y-%m-%d %H:%M:%S')
|
||
user_data.append(user_info)
|
||
|
||
return Response({
|
||
'code': 200,
|
||
'message': '获取用户列表成功',
|
||
'data': {
|
||
'total': total,
|
||
'page': page,
|
||
'page_size': page_size,
|
||
'users': user_data
|
||
}
|
||
})
|
||
|
||
except ValueError as e:
|
||
return Response({
|
||
'code': 400,
|
||
'message': f'参数错误: {str(e)}',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取用户列表失败: {str(e)}")
|
||
logger.error(traceback.format_exc())
|
||
return Response({
|
||
'code': 500,
|
||
'message': f'获取用户列表失败: {str(e)}',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
class UserGoalView(APIView):
|
||
"""
|
||
用户目标管理API
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def get(self, request):
|
||
"""获取当前用户的所有目标"""
|
||
try:
|
||
goals = UserGoal.objects.filter(user=request.user, is_active=True)
|
||
serializer = UserGoalSerializer(goals, many=True)
|
||
return Response({
|
||
'code': 200,
|
||
'message': '获取目标列表成功',
|
||
'data': serializer.data
|
||
})
|
||
except Exception as e:
|
||
logger.error(f"获取用户目标失败: {str(e)}")
|
||
logger.error(traceback.format_exc())
|
||
return Response({
|
||
'code': 500,
|
||
'message': f'获取用户目标失败: {str(e)}',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
def post(self, request):
|
||
"""创建新的用户目标"""
|
||
try:
|
||
serializer = UserGoalSerializer(data=request.data, context={'request': request})
|
||
if serializer.is_valid():
|
||
serializer.save()
|
||
return Response({
|
||
'code': 201,
|
||
'message': '目标创建成功',
|
||
'data': serializer.data
|
||
}, status=status.HTTP_201_CREATED)
|
||
|
||
return Response({
|
||
'code': 400,
|
||
'message': '创建目标失败',
|
||
'data': serializer.errors
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
except Exception as e:
|
||
logger.error(f"创建用户目标失败: {str(e)}")
|
||
logger.error(traceback.format_exc())
|
||
return Response({
|
||
'code': 500,
|
||
'message': f'创建用户目标失败: {str(e)}',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
|
||
class UserGoalDetailView(APIView):
|
||
"""
|
||
用户目标详情API
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def get_object(self, goal_id, user):
|
||
"""获取指定的用户目标"""
|
||
try:
|
||
# 验证UUID格式
|
||
uuid_obj, error_response = validate_uuid_param(goal_id)
|
||
if error_response:
|
||
return None
|
||
|
||
return UserGoal.objects.get(id=uuid_obj, user=user)
|
||
except UserGoal.DoesNotExist:
|
||
return None
|
||
|
||
def get(self, request, goal_id):
|
||
"""获取单个目标详情"""
|
||
try:
|
||
goal = self.get_object(goal_id, request.user)
|
||
if not goal:
|
||
return Response({
|
||
'code': 404,
|
||
'message': '目标不存在',
|
||
'data': None
|
||
}, status=status.HTTP_404_NOT_FOUND)
|
||
|
||
serializer = UserGoalSerializer(goal)
|
||
return Response({
|
||
'code': 200,
|
||
'message': '获取目标详情成功',
|
||
'data': serializer.data
|
||
})
|
||
except Exception as e:
|
||
logger.error(f"获取目标详情失败: {str(e)}")
|
||
logger.error(traceback.format_exc())
|
||
return Response({
|
||
'code': 500,
|
||
'message': f'获取目标详情失败: {str(e)}',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
def put(self, request, goal_id):
|
||
"""更新目标信息"""
|
||
try:
|
||
goal = self.get_object(goal_id, request.user)
|
||
if not goal:
|
||
return Response({
|
||
'code': 404,
|
||
'message': '目标不存在',
|
||
'data': None
|
||
}, status=status.HTTP_404_NOT_FOUND)
|
||
|
||
serializer = UserGoalSerializer(goal, data=request.data)
|
||
if serializer.is_valid():
|
||
serializer.save()
|
||
return Response({
|
||
'code': 200,
|
||
'message': '目标更新成功',
|
||
'data': serializer.data
|
||
})
|
||
|
||
return Response({
|
||
'code': 400,
|
||
'message': '更新目标失败',
|
||
'data': serializer.errors
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
except Exception as e:
|
||
logger.error(f"更新目标失败: {str(e)}")
|
||
logger.error(traceback.format_exc())
|
||
return Response({
|
||
'code': 500,
|
||
'message': f'更新目标失败: {str(e)}',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
def delete(self, request, goal_id):
|
||
"""删除目标"""
|
||
try:
|
||
goal = self.get_object(goal_id, request.user)
|
||
if not goal:
|
||
return Response({
|
||
'code': 404,
|
||
'message': '目标不存在',
|
||
'data': None
|
||
}, status=status.HTTP_404_NOT_FOUND)
|
||
|
||
# 软删除: 将状态设置为非活跃
|
||
goal.is_active = False
|
||
goal.save()
|
||
|
||
return Response({
|
||
'code': 200,
|
||
'message': '目标删除成功',
|
||
'data': None
|
||
})
|
||
except Exception as e:
|
||
logger.error(f"删除目标失败: {str(e)}")
|
||
logger.error(traceback.format_exc())
|
||
return Response({
|
||
'code': 500,
|
||
'message': f'删除目标失败: {str(e)}',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
class RecommendedReplyView(APIView):
|
||
"""
|
||
基于用户目标生成推荐回复话术的API
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
"""
|
||
生成推荐回复话术
|
||
|
||
请求参数:
|
||
- goal_id: 目标ID (可选,如不提供则使用用户当前活跃目标)
|
||
- conversation_id: 对话ID (可选,用于获取对话摘要和最后一条消息)
|
||
- conversation_summary: 对话摘要 (可选,如提供了conversation_id则优先使用对话ID获取摘要)
|
||
- last_message: 达人最后发送的消息内容 (可选,如提供了conversation_id则可以自动获取)
|
||
|
||
响应:
|
||
- 推荐的回复话术
|
||
"""
|
||
try:
|
||
# 获取请求参数
|
||
goal_id = request.data.get('goal_id')
|
||
conversation_id = request.data.get('conversation_id')
|
||
conversation_summary = request.data.get('conversation_summary', '')
|
||
last_message = request.data.get('last_message')
|
||
|
||
# 如果提供了对话ID,尝试获取对话摘要和最后一条消息
|
||
if conversation_id:
|
||
# 获取对话摘要
|
||
stored_summary = get_conversation_summary(conversation_id)
|
||
if stored_summary:
|
||
conversation_summary = stored_summary
|
||
|
||
# 如果没有提供last_message,尝试从对话中获取
|
||
if not last_message:
|
||
last_message = get_last_message(conversation_id)
|
||
|
||
# 验证必填参数
|
||
if not last_message:
|
||
return Response({
|
||
'code': 400,
|
||
'message': '缺少必要参数: last_message,且无法从对话ID自动获取最后一条消息',
|
||
'data': None
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 获取用户目标
|
||
if goal_id:
|
||
# 如果提供了目标ID,则获取该目标
|
||
uuid_obj, error_response = validate_uuid_param(goal_id)
|
||
if error_response:
|
||
return error_response
|
||
|
||
try:
|
||
goal = UserGoal.objects.get(id=uuid_obj, user=request.user, is_active=True)
|
||
goal_description = goal.description
|
||
except UserGoal.DoesNotExist:
|
||
return Response({
|
||
'code': 404,
|
||
'message': '目标不存在',
|
||
'data': None
|
||
}, status=status.HTTP_404_NOT_FOUND)
|
||
else:
|
||
# 否则使用用户最近的活跃目标
|
||
goal = get_active_goal(request.user)
|
||
|
||
if not goal:
|
||
return Response({
|
||
'code': 404,
|
||
'message': '未找到活跃目标,请先设置目标',
|
||
'data': None
|
||
}, status=status.HTTP_404_NOT_FOUND)
|
||
|
||
goal_description = goal.description
|
||
|
||
# 生成推荐回复
|
||
reply_content, error = generate_recommended_reply(
|
||
request.user,
|
||
goal_description,
|
||
conversation_summary,
|
||
last_message
|
||
)
|
||
|
||
if error:
|
||
return Response({
|
||
'code': 500,
|
||
'message': f'生成推荐回复失败: {error}',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
return Response({
|
||
'code': 200,
|
||
'message': '推荐回复生成成功',
|
||
'data': {
|
||
'goal_id': str(goal.id),
|
||
'goal_description': goal_description,
|
||
'recommended_reply': reply_content,
|
||
'conversation_id': conversation_id
|
||
}
|
||
})
|
||
|
||
except Exception as e:
|
||
logger.error(f"生成推荐回复失败: {str(e)}")
|
||
logger.error(traceback.format_exc())
|
||
return Response({
|
||
'code': 500,
|
||
'message': f'服务器错误: {str(e)}',
|
||
'data': None
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|