forked from erp-dev/erp
213 lines
6.7 KiB
Python
213 lines
6.7 KiB
Python
"""
|
||
企业微信 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,
|
||
)
|