forked from erp-dev/erp
fea: business completed
This commit is contained in:
212
api_v2/views/wecom.py
Normal file
212
api_v2/views/wecom.py
Normal file
@@ -0,0 +1,212 @@
|
||||
"""
|
||||
企业微信 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,
|
||||
)
|
||||
Reference in New Issue
Block a user