1
0
forked from erp-dev/erp

fea: business completed

This commit is contained in:
2026-06-06 14:14:50 +08:00
parent b97e86257e
commit 3b2aacaa5c
12 changed files with 1270 additions and 92 deletions

212
api_v2/views/wecom.py Normal file
View 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,
)