企业级简道云工作流自动化与飞书消息通知解决方案
简道云消息集成推送到飞书系统是企业级工作流自动化解决方案,实现了简道云工作流与飞书消息的无缝集成。该系统能够接收简道云Webhook通知,根据工作流映射配置将消息发送到对应的飞书群,并支持@特定用户。系统同时支持内部员工通过飞书Open ID接收消息和外部用户通过邮箱接收消息,构建了完整的跨平台工作流通知体系。
该系统适用于需要将简道云工作流通知自动化发送到飞书的企业场景,特别是:
| 技术类别 | 具体技术 | 用途 |
|---|---|---|
| 后端框架 | Python Flask | 构建Web服务和API |
| 数据库 | JSON文件存储 | 存储员工信息和配置 |
| 简道云API | 简道云开放平台API | 获取用户信息和工作流数据 |
| 飞书API | 飞书开放平台API | 发送消息和获取用户信息 |
| 部署方式 | 独立服务器部署 | 提供稳定的Webhook服务 |
简道云消息集成推送到飞书系统采用模块化设计,各组件之间通过明确的接口通信,实现了高内聚、低耦合的架构。系统主要包含以下核心组件:
要使用简道云消息集成功能,首先需要在简道云开放平台进行相关配置:
在简道云工作流设计界面,需要配置以下内容:
http://your-server-ip:3100/jiandaoyun/webhook
系统支持通过脚本批量更新员工的简道云ID:
# 员工简道云编号映射
taiwan_staff_jdy_map = {
"叶XX": "3b82a585",
"唐XX": "989g4394",
"王XX": "91b4a866",
# ... 更多员工映射
};
# 批量更新简道云ID的逻辑
def update_feishu_open_ids():
# 读取feishu_open_ids.js文件
# 批量更新员工的简道云ID
# 保存更新后的文件
pass
# 运行update_jdy_ids.py脚本更新简道云ID
python3 update_jdy_ids.py
要使用飞书消息集成功能,首先需要在飞书开放平台进行相关配置:
在应用详情页面,需要配置以下权限:
在应用详情页面,获取以下凭证:
对于群消息发送,需要在飞书群中添加机器人:
| 配置文件 | 用途 | 说明 |
|---|---|---|
| feishu_open_ids.js | 员工信息配置 | 存储员工姓名、飞书Open ID、邮箱和简道云ID的映射关系 |
| feishu_token.js | 飞书Token配置 | 存储飞书API调用所需的tenant_access_token |
| workflow_chat_mappings.js | 工作流映射配置 | 配置简道云工作流名称与飞书群的对应关系 |
const FEISHU_OPEN_IDS = [
{
"name": "张三",
"open_id": "ou-xxx",
"email": "zhangsan@example.com",
"jdy_id": "123456",
"update_date": "2026-01-12"
},
{
"name": "李四",
"open_id": "ou-yyy",
"email": "lisi@example.com",
"jdy_id": "",
"update_date": "2026-01-12"
}
];
const WORKFLOW_CHAT_MAPPINGS = {
"面试信息": {
"webhook_url": "https://open.feishu.cn/open-apis/bot/v2/hook/xxx",
"chat_id": "oc_xxx"
},
"请假审批": {
"webhook_url": "https://open.feishu.cn/open-apis/bot/v2/hook/yyy",
"chat_id": "oc_yyy"
}
};
简道云Webhook请求包含以下关键信息:
{
"event": "workflow_trigger",
"timestamp": 1640995200,
"data": {
"workflow_name": "面试信息",
"entry_id": "entry_123456",
"to": "李四",
"from": "张三",
"status": "pending",
"url": "https://www.jiandaoyun.com/form/entry/123456",
"fields": {
"field1": "value1",
"field2": "value2"
}
}
}
系统通过workflow_chat_mappings.js文件实现简道云工作流与飞书群的映射,支持以下特性:
@app.route('/jiandaoyun/webhook', methods=['POST'])
def jiandaoyun_webhook():
"""接收简道云Webhook通知"""
try:
# 接收并解析Webhook数据
data = request.get_json()
# 验证Webhook签名(可选)
# verify_signature(data)
# 提取关键信息
workflow_name = data.get('data', {}).get('workflow_name', '')
entry_id = data.get('data', {}).get('entry_id', '')
to_user = data.get('data', {}).get('to', '')
from_user = data.get('data', {}).get('from', '')
status = data.get('data', {}).get('status', '')
url = data.get('data', {}).get('url', '')
# 根据工作流名称匹配飞书群配置
workflow_config = get_workflow_config(workflow_name)
if not workflow_config:
print(f"未找到工作流配置: {workflow_name}")
return jsonify({"code": 404, "msg": "Workflow not found"}), 404
# 处理消息发送逻辑
# ... 省略消息发送逻辑
return jsonify({"code": 0, "msg": "success"}), 200
except Exception as e:
print(f"Webhook处理异常: {str(e)}")
return jsonify({"code": 500, "msg": "Internal server error"}), 500
| 消息类型 | 描述 | 使用场景 | 实现方式 |
|---|---|---|---|
| Webhook消息 | 通过飞书群机器人发送的文本消息 | 群内通知、@提醒 | 调用飞书群机器人Webhook |
| 卡片消息 | 通过飞书API发送的交互式卡片 | 详细通知、操作引导 | 调用飞书ephemeral API |
系统采用以下策略匹配飞书用户:
通过简道云ID直接匹配飞书用户,这是最准确的匹配方式。
如果jdy_id匹配失败,则根据用户名进行匹配,优先匹配jdy_id为空的记录,避免姓名冲突。
内部用户通过飞书Open ID接收消息,外部用户通过邮箱接收消息。
def get_user_contact_by_jdy_username(jdy_username, jdy_payload=None):
"""根据简道云用户名获取对应的飞书联系方式(open_id或email)"""
# 1. 优先根据jdy_id匹配
for staff in feishu_data:
if staff.get('jdy_id') == jdy_username:
open_id = staff.get('open_id')
email = staff.get('email')
# 优先使用open_id,只有当open_id不存在时才使用email
if open_id:
return {'type': 'open_id', 'value': open_id}
elif email:
return {'type': 'email', 'value': email}
else:
return None
# 2. 如果jdy_id匹配失败,且提供了jdy_payload,则根据name匹配
if jdy_payload:
name = jdy_payload.get('data', {}).get('to', '')
if name:
# 先查找jdy_id为空且name匹配的记录
for staff in feishu_data:
if staff.get('name') == name and staff.get('jdy_id') == '':
open_id = staff.get('open_id')
email = staff.get('email')
if open_id:
return {'type': 'open_id', 'value': open_id}
elif email:
return {'type': 'email', 'value': email}
else:
return None
# 如果没有找到jdy_id为空的记录,再查找所有name匹配的记录
for staff in feishu_data:
if staff.get('name') == name:
open_id = staff.get('open_id')
email = staff.get('email')
if open_id:
return {'type': 'open_id', 'value': open_id}
elif email:
return {'type': 'email', 'value': email}
else:
return None
return None
def send_feishu_webhook_message(webhook_url, contact, entry_name):
"""通过飞书机器人Webhook发送@提醒消息"""
# 根据联系方式类型构建不同的@语法
if contact['type'] == 'open_id':
at_syntax = f" "
else: # email类型
at_syntax = f" "
# 构建Webhook消息,包含@提醒
webhook_payload = {
"msg_type": "text",
"content": {
"text": f"{at_syntax} 你有新的待办:{entry_name},请查看"
}
}
# 发送Webhook请求
try:
response = requests.post(webhook_url, json=webhook_payload, timeout=10)
response.raise_for_status()
result = response.json()
if result.get('code') == 0:
return True
else:
print(f"Webhook消息发送失败: {result.get('msg')}")
return False
except Exception as e:
print(f"Webhook请求异常: {str(e)}")
return False
def send_feishu_ephemeral_message(tenant_access_token, chat_id, contact, entry_name, notify_text, url):
"""发送仅特定人可见的飞书消息卡片"""
# 构建卡片消息内容
card = {
"config": {
"wide_screen_mode": True
},
"elements": [
{
"tag": "div",
"text": {
"tag": "lark_md",
"content": notify_text
}
},
{
"tag": "div",
"actions": [
{
"tag": "button",
"text": {
"tag": "plain_text",
"content": "查看详情"
},
"type": "primary",
"url": url
}
]
}
],
"header": {
"title": {
"content": entry_name,
"tag": "plain_text"
},
"type": "blue"
}
}
# 构建请求体
payload = {
"chat_id": chat_id,
"msg_type": "interactive",
"card": card
}
# 根据联系方式类型添加对应的字段
if contact['type'] == 'open_id':
payload['open_id'] = contact['value']
else: # email类型
payload['email'] = contact['value']
# 发送API请求
headers = {
"Authorization": f"Bearer {tenant_access_token}",
"Content-Type": "application/json"
}
try:
response = requests.post(
"https://open.feishu.cn/open-apis/ephemeral/v1/send",
json=payload,
headers=headers,
timeout=10
)
response.raise_for_status()
result = response.json()
if result.get('code') == 0:
return True
else:
print(f"卡片消息发送失败: {result.get('msg')}")
return False
except Exception as e:
print(f"卡片消息请求异常: {str(e)}")
return False
系统提供了Web界面用于管理员工信息,访问地址为:
http://your-server-ip:3100/staff
| 字段名 | 类型 | 用途 | 是否必填 |
|---|---|---|---|
| name | 字符串 | 员工姓名 | 是 |
| jdy_id | 字符串 | 简道云ID | 否 |
| open_id | 字符串 | 飞书Open ID | 否(与email二选一) |
| 字符串 | 员工邮箱 | 否(与open_id二选一) | |
| update_date | 日期 | 更新日期 | 自动生成 |
系统支持通过脚本批量更新员工信息:
# 运行get_feishu_open_ids.py脚本更新飞书Open ID
python3 get_feishu_open_ids.py
# 运行get_jdy_user_info.py脚本同步简道云用户信息
python3 get_jdy_user_info.py
系统允许添加姓名相同但简道云ID或飞书ID不同的员工记录,通过以下机制实现:
{"code": 0, "msg": "success", "data": {"user_id": "123456", "name": "张三", "email": "zhangsan@example.com"}}
{"code": 0, "msg": "success", "data": {"workflows": [{"workflow_id": "wf_123456", "name": "面试信息"}], "total": 1}}
{"code": 0, "msg": "success", "tenant_access_token": "t-xxx", "expire": 7200}
{"code": 0, "msg": "success", "data": {"message_id": "om-xxx"}}
包含工作流映射配置的字典:
{"webhook_url": "https://open.feishu.cn/open-apis/bot/v2/hook/xxx", "chat_id": "oc_xxx"}
包含联系方式类型和值的字典:
{"type": "open_id", "value": "ou-xxx"}
布尔值,表示消息是否发送成功
问题描述: 简道云发送的Webhook请求未能被系统接收。
可能原因及解决方案:
检查服务器网络连接,确保能够接收外部请求。
检查服务器防火墙配置,确保端口3100(或自定义端口)开放。
检查简道云Webhook配置的URL是否正确,确保指向系统的公网地址。
检查系统服务是否正常运行,确保Webhook监听端口正常。
问题描述: 系统无法根据工作流名称匹配对应的飞书群配置。
解决方案:
问题描述: 系统无法根据简道云用户名匹配对应的飞书用户。
解决方案:
问题描述: 调用飞书API发送消息失败。
可能原因及解决方案:
检查feishu_token.js中的token是否有效,尝试重新生成token。
检查飞书应用是否已配置所需权限,确保权限已生效。
检查用户信息是否正确,特别是open_id和email字段。
检查服务器网络连接,确保能够访问飞书API。
问题描述: 发送的消息中@提醒没有正确显示。
解决方案:
<at open_id="open_id"></at>或<at email="email"></at>