baodan/api/insurance/wecom/routes.py
2026-07-12 14:17:18 +08:00

111 lines
4.4 KiB
Python
Raw 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.

"""企微机器人路由
GET /wecom/callback 企微 URL 验证
POST /wecom/callback 企微消息接收
GET /wecom/oauth 企微 OAuth 登录入口
GET /wecom/oauth/callback 企微 OAuth 回调
"""
from flask import Blueprint, request, Response, redirect
from insurance.config import get_config as _get_wecom_config
from insurance.wecom.service import WeComService
wecom_bp = Blueprint("wecom", __name__)
wecom_service = WeComService()
@wecom_bp.route("/callback", methods=["GET"])
def verify_callback():
"""企微服务器 URL 验证GET"""
msg_signature = request.args.get("msg_signature", "")
timestamp = request.args.get("timestamp", "")
nonce = request.args.get("nonce", "")
echostr = request.args.get("echostr", "")
result = wecom_service.verify_url(msg_signature, timestamp, nonce, echostr)
return Response(result, content_type="text/plain")
@wecom_bp.route("/callback", methods=["POST"])
def receive_message():
"""企微消息接收POST
群聊/私聊:立即返回 "success",异步调 BaoDan 并主动发送回复。
"""
# 校验回调IP白名单Nginx透传 X-Forwarded-For
client_ip = request.headers.get("X-Forwarded-For", request.remote_addr or "").split(",")[0].strip()
if not wecom_service.is_callback_ip(client_ip):
print(f"[WECOM ERROR] 非法回调IP: {client_ip}", flush=True)
return Response("", status=200)
msg_signature = request.args.get("msg_signature", "")
timestamp = request.args.get("timestamp", "")
nonce = request.args.get("nonce", "")
encrypted_data = request.data
# 先判断是否群聊(群聊必须立即返回 "success"5秒内不回复会被企微重试
if wecom_service.handle_message_for_group(msg_signature, timestamp, nonce, encrypted_data):
return Response("success", status=200)
if wecom_service.handle_message_for_private(msg_signature, timestamp, nonce, encrypted_data):
return Response("success", status=200)
# 兜底:同步处理,返回被动回复 XML
reply_xml = wecom_service.handle_message(msg_signature, timestamp, nonce, encrypted_data)
if reply_xml:
return Response(reply_xml, content_type="application/xml", status=200)
return Response("", status=200)
@wecom_bp.route("/oauth", methods=["GET"])
def oauth_login():
"""重定向到企微授权页面。"""
from urllib.parse import quote
corp_id = _get_wecom_config("WECOM_CORP_ID")
# 优先使用 BASE_URL 环境变量Docker/Vite 代理下 request.host_url 是内部地址)
base_url = _get_wecom_config("BASE_URL", "").rstrip("/")
if not base_url:
base_url = request.host_url.rstrip("/")
redirect_uri = f"{base_url}/insurance/wecom/oauth/callback"
auth_url = (
f"https://open.weixin.qq.com/connect/oauth2/authorize"
f"?appid={corp_id}"
f"&redirect_uri={quote(redirect_uri, safe='')}"
f"&response_type=code"
f"&scope=snsapi_userinfo"
f"&state=state"
f"#wechat_redirect"
)
return redirect(auth_url)
@wecom_bp.route("/oauth/callback", methods=["GET"])
def oauth_callback():
"""企微 OAuth 回调code 换用户信息 -> 生成 JWT -> 重定向到前端。"""
code = request.args.get("code", "")
if not code:
frontend_url = _get_wecom_config("FRONTEND_URL", "").rstrip("/")
if frontend_url:
return redirect(f"{frontend_url}/login?error=auth_failed")
return redirect("/login?error=auth_failed")
from insurance.auth.service import AuthService
auth_service = AuthService()
result = auth_service.wework_login(code, "")
if result.get("code") == 0:
token = result["data"]["token"]
user = result["data"].get("user", {})
import json
user_json = json.dumps(user, ensure_ascii=False)
from urllib.parse import quote
# 重定向到前端Docker 中后端和前端端口不同)
frontend_url = _get_wecom_config("FRONTEND_URL", "").rstrip("/")
if frontend_url:
return redirect(f"{frontend_url}/?token={token}&user={quote(user_json)}")
return redirect(f"/?token={token}&user={quote(user_json)}")
else:
frontend_url = _get_wecom_config("FRONTEND_URL", "").rstrip("/")
if frontend_url:
return redirect(f"{frontend_url}/login?error=auth_failed")
return redirect("/login?error=auth_failed")