1
0
forked from erp-dev/erp
Files
erpnew/api_v2/views/wecom.py
2026-06-06 14:14:50 +08:00

213 lines
6.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
企业微信 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,
)