""" 企业微信 API 供外部企业微信服务端程序调用: 1. 绑定接口:将企业微信 user_id 与本系统 Employee 绑定 2. 登录接口:通过已绑定的 wecom_user_id 快捷获取 JWT token 认证方式:AGENT_ACCESS_KEY(通过 Authorization 头传入)。 """ import logging from django.contrib.auth import authenticate from rest_framework import serializers, status from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from rest_framework_simplejwt.tokens import RefreshToken from api_v2.views.ai import AgentAccessKeyAuthentication from basic_info.models import Employee logger = logging.getLogger(__name__) class WecomBindingRequestSerializer(serializers.Serializer): wecom_user_id = serializers.CharField( max_length=128, help_text='企业微信 OAuth2 返回的 user_id', ) username = serializers.CharField( max_length=150, help_text='本系统用户名', ) password = serializers.CharField( max_length=128, help_text='本系统用户密码', ) force_update = serializers.BooleanField( default=False, required=False, help_text='如果该 Employee 已绑定其他企业微信用户,是否强制覆盖', ) class WecomBindingView(APIView): """ 企业微信用户绑定接口。 POST /api/v2/wecom/binduser/ 请求体: - wecom_user_id: 企业微信用户 ID - username: 本系统用户名 - password: 本系统用户密码 - force_update: (可选) 是否强制覆盖已有绑定,默认 false 认证:Authorization 头传入 AGENT_ACCESS_KEY """ authentication_classes = [AgentAccessKeyAuthentication] permission_classes = [IsAuthenticated] def post(self, request): ser = WecomBindingRequestSerializer(data=request.data) ser.is_valid(raise_exception=True) wecom_user_id = ser.validated_data['wecom_user_id'] username = ser.validated_data['username'] password = ser.validated_data['password'] force_update = ser.validated_data.get('force_update', False) # 1. 验证本系统用户凭据 user = authenticate(username=username, password=password) if user is None: return Response( {'error': '用户名或密码错误'}, status=status.HTTP_401_UNAUTHORIZED, ) if not user.is_active: return Response( {'error': '该用户已被禁用'}, status=status.HTTP_403_FORBIDDEN, ) # 2. 查找关联的 Employee employee = Employee.objects.filter(sys_user=user).first() if employee is None: return Response( {'error': '该用户未关联任何员工记录'}, status=status.HTTP_404_NOT_FOUND, ) # 3. 检查该 wecom_user_id 是否已被其他 Employee 绑定 existing = Employee.objects.filter(wecom_user_id=wecom_user_id).exclude(id=employee.id).first() if existing is not None: return Response( {'error': f'该企业微信用户已绑定到其他员工: {existing.name} (ID: {existing.id})'}, status=status.HTTP_409_CONFLICT, ) # 4. 检查该 Employee 是否已有不同的 wecom_user_id if employee.wecom_user_id and employee.wecom_user_id != wecom_user_id: if not force_update: return Response( { 'error': '该员工已绑定其他企业微信用户', 'current_wecom_user_id': employee.wecom_user_id, 'hint': '如需覆盖,请传入 force_update=true', }, status=status.HTTP_409_CONFLICT, ) # 5. 执行绑定 employee.wecom_user_id = wecom_user_id employee.save(update_fields=['wecom_user_id', 'updated_at']) logger.info( 'WeChat Work binding success: wecom_user_id=%s -> employee=%s (ID: %s)', wecom_user_id, employee.name, employee.id, ) return Response( { 'message': '绑定成功', 'employee_id': employee.id, 'employee_name': employee.name, 'wecom_user_id': wecom_user_id, }, status=status.HTTP_200_OK, ) class WecomLoginRequestSerializer(serializers.Serializer): wecom_user_id = serializers.CharField( max_length=128, help_text='企业微信 OAuth2 返回的 user_id', ) class WecomLoginView(APIView): """ 企业微信快捷登录接口。 POST /api/v2/wecom/login/ 通过已绑定的 wecom_user_id 获取本系统的 JWT token, 无需再次输入用户名密码。 请求体: - wecom_user_id: 企业微信用户 ID(必须已通过绑定接口完成绑定) 认证:Authorization 头传入 AGENT_ACCESS_KEY """ authentication_classes = [AgentAccessKeyAuthentication] permission_classes = [IsAuthenticated] def post(self, request): ser = WecomLoginRequestSerializer(data=request.data) ser.is_valid(raise_exception=True) wecom_user_id = ser.validated_data['wecom_user_id'] # 1. 通过 wecom_user_id 查找 Employee employee = Employee.objects.select_related('sys_user').filter( wecom_user_id=wecom_user_id, ).first() if employee is None: return Response( {'error': '该企业微信用户未绑定本系统员工'}, status=status.HTTP_404_NOT_FOUND, ) # 2. 检查关联的系统用户 user = employee.sys_user if user is None: return Response( {'error': '该员工未关联系统用户'}, status=status.HTTP_404_NOT_FOUND, ) if not user.is_active: return Response( {'error': '该用户已被禁用'}, status=status.HTTP_403_FORBIDDEN, ) # 3. 生成 JWT token refresh = RefreshToken.for_user(user) logger.info( 'WeChat Work login success: wecom_user_id=%s -> user=%s (employee=%s)', wecom_user_id, user.username, employee.name, ) return Response( { 'access': str(refresh.access_token), 'refresh': str(refresh), 'employee_id': employee.id, 'employee_name': employee.name, 'username': user.username, }, status=status.HTTP_200_OK, )