2025-06-17 17:38:29.495 INFO 24792 --- [nio-8080-exec-4] org.example.MomentsBitableIntegration : 解密后事件数据: {"schema":"2.0","header":{"event_id":"9f1e4a32919fe9d8cb031699e23a03e5","token":"1OJhtUAd0gvlUnyCMPVLdg68bFaPRLLo","create_time":"1750153109867","event_type":"moments.reaction.created_v1","tenant_key":"1674d8abf05b975f","app_id":"cli_a77e623b63fbd00d"},"event":{"create_time":"2025-06-17T17:38:28 08:00","entity_id":"7516849034822385668","entity_type":1,"id":"7516850365085270019","type":"THUMBSUP","user_id":{"open_id":"ou_ac7cb4342f4beb42223c44ddb9bbc980","union_id":"on_0797b4d5356564d63e4ccc22a3eb826c","user_id":"os-zhanghuanzhou"},"user_type":1}} 2025-06-17 17:38:29.739 INFO 24792 --- [nio-8080-exec-4] org.example.MomentsBitableIntegration : 从user_id字段获取作者ID: ou_d2e72d80db91c722cb2b3c1dd12cb625 2025-06-17 17:38:29.739 INFO 24792 --- [nio-8080-exec-4] org.example.MomentsBitableIntegration : 获取到帖子作者ID: ou_d2e72d80db91c722cb2b3c1dd12cb625 (来源: user_id) 2025-06-17 17:38:29.739 INFO 24792 --- [nio-8080-exec-4] org.example.MomentsBitableIntegration : 调用用户信息API: https://open.feishu.cn/open-apis/contact/v3/users/ou_d2e72d80db91c722cb2b3c1dd12cb625?department_id_type=open_department_id
时间: 2025-06-17 18:13:52 浏览: 13
<think>我们正在处理飞书开放平台的moments.reaction.created_v1事件。该事件在用户对某个动态(moment)做出反应(如点赞)时触发。我们需要:1.从日志中获取事件数据(通常是加密的)2.解密事件数据3.解析事件数据以提取用户ID(可能是触发反应的用户ID,也可能是动态作者的用户ID,根据需求而定)4.使用提取的用户ID调用用户信息API获取详细信息步骤详解:###1.获取事件日志飞书开放平台会将事件通过HTTPPOST请求发送到我们预先配置的请求地址(CallbackURL)。请求体是加密的,格式如下:```json{"encrypt":"加密的字符串"}```###2.解密事件数据飞书使用AES加密算法对事件数据进行加密。解密步骤如下:-获取加密密钥(EncryptionKey)和验证令牌(VerificationToken):在开发者后台的应用事件订阅设置中获取。-使用AES解密算法,模式为CBC,填充方式为PKCS#7。解密后得到JSON字符串。解密后的JSON结构示例(moments.reaction.created_v1事件):```json{"schema":"2.0","header":{"event_id":"***","event_type":"moments.reaction.created_v1","create_time":"***","token":"***",//验证令牌"app_id":"***","tenant_key":"***"},"event":{"reaction_type":"LIKE",//反应类型,例如点赞"operator":{"user_id":"ou_***"//做出反应的用户ID},"moment_id":"***",//动态ID"operator_id":{//操作人ID(与operator.user_id相同)"user_id":"ou_***","open_id":"***","union_id":"***"}}}```###3.提取用户ID从解密后的事件数据中,我们关注的是操作者(即做出反应的用户)的ID。在`event.operator.user_id`字段中,我们可以提取到用户ID(格式为`ou_xxx`)。###4.调用用户信息API使用飞书开放平台的[获取用户信息API](https://open.feishu.cn/document/server-docs/contact-v3/user/get)根据用户ID获取用户详细信息。API端点:`GEThttps://open.feishu.cn/open-apis/contact/v3/users/{user_id}`其中`{user_id}`就是上面提取的`user_id`(注意:这里需要的是`user_id`类型,即`ou_xxx`)。请求头需要包含Authorization(访问令牌):```Authorization:Bearer{access_token}```响应示例:```json{"code":0,"data":{"user":{"user_id":"ou_***","name":"张三","mobile":"***",//其他字段...}}}```###实现代码示例(Python)假设我们使用Flask框架接收事件:```pythonfromflaskimportFlask,request,jsonifyimportjsonfromcryptography.hazmat.primitives.ciphersimportCipher,algorithms,modesfromcryptography.hazmat.primitivesimportpaddingfromcryptography.hazmat.backendsimportdefault_backendimportbase64importrequestsapp=Flask(__name__)#飞书应用配置APP_ID="your_app_id"ENCRYPT_KEY="your_encrypt_key"#在事件订阅页面获取的EncryptionKeyVERIFICATION_TOKEN="your_verification_token"#验证令牌#飞书API配置APP_ID_FOR_API=APP_IDAPP_SECRET="your_app_secret"defdecrypt_data(encrypt_key,data):#base64解码decoded=base64.b64decode(data)#提取iv和密文iv=decoded[:16]ciphertext=decoded[16:]#初始化AES解密器backend=default_backend()cipher=Cipher(algorithms.AES(encrypt_key.encode('utf-8')),modes.CBC(iv),backend=backend)decryptor=cipher.decryptor()#解密plaintext=decryptor.update(ciphertext)+decryptor.finalize()#去除PKCS#7填充unpadder=padding.PKCS7(128).unpadder()unpadded_data=unpadder.update(plaintext)+unpadder.finalize()returnunpadded_data.decode('utf-8')defget_access_token(app_id,app_secret):url="https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/"headers={"Content-Type":"application/json;charset=utf-8"}payload={"app_id":app_id,"app_secret":app_secret}response=requests.post(url,headers=headers,json=payload)returnresponse.json()['tenant_access_token']@app.route('/webhook',methods=['POST'])defwebhook():#获取加密事件数据encrypted_data=request.json.get('encrypt')ifnotencrypted_data:returnjsonify({'error':'Missingencryptdata'}),400#解密try:decrypted_json=decrypt_data(ENCRYPT_KEY,encrypted_data)event_data=json.loads(decrypted_json)exceptExceptionase:returnjsonify({'error':str(e)}),400#验证token(可选,但推荐)token=event_data.get('header',{}).get('token')iftoken!=VERIFICATION_TOKEN:returnjsonify({'error':'Invalidtoken'}),401#处理特定事件类型ifevent_data['header']['event_type']=='moments.reaction.created_v1':#提取操作者用户IDoperator_user_id=event_data['event']['operator']['user_id']#获取访问令牌access_token=get_access_token(APP_ID_FOR_API,APP_SECRET)#调用用户信息APIuser_info_url=f"https://open.feishu.cn/open-apis/contact/v3/users/{operator_user_id}"headers={"Authorization":f"Bearer{access_token}","Content-Type":"application/json"}response=requests.get(user_info_url,headers=headers)ifresponse.status_code==200:user_data=response.json()#处理用户数据...#例如,记录或进一步处理returnjsonify({'status':'success','user_info':user_data})else:returnjsonify({'error':'Failedtogetuserinfo','response':response.text}),500returnjsonify({'status':'success'})if__name__=='__main__':app.run(port=5000)```注意:-解密函数中,我们假设飞书的加密数据是Base64编码的,解密后是JSON字符串。-获取访问令牌需要应用的app_id和app_secret,该令牌有效期为2小时,实际应用中应考虑缓存令牌。-事件处理中,我们只处理了`moments.reaction.created_v1`事件,其他事件类型可类似扩展。###安全性-验证令牌(VERIFICATION_TOKEN)用于验证事件来源的合法性,建议在解密后验证。-使用HTTPS确保数据传输安全。###引用说明飞书开放平台事件订阅文档:[事件订阅概述](https://open.feishu.cn/document/ukTMukTMukTM/uUTNz4SN1MjL1UzM)[^1]用户信息API文档:[获取用户信息](https://open.feishu.cn/document/server-docs/contact-v3/user/get)[^2]§§相关问题§§1.如何验证飞书事件订阅的请求来源?2.飞书事件解密失败的可能原因有哪些?3.如何缓存飞书的访问令牌以避免频繁获取?4.除了用户信息,飞书用户API还能获取哪些信息?</think>### 解析和处理飞书开放平台 `moments.reaction.created_v1` 事件数据的完整流程
#### 1. **事件数据结构解析**
`moments.reaction.created_v1` 事件在用户对动态(moment)进行点赞/反应时触发,其核心数据结构如下:
```json
{
"schema": "2.0",
"header": {
"event_id": "f79c****",
"event_type": "moments.reaction.created_v1",
"create_time": "165873****",
"token": "验证令牌",
"app_id": "cli_****",
"tenant_key": "企业ID"
},
"event": {
"reaction_type": "LIKE", // 反应类型(如点赞)
"operator": {
"user_id": "ou_****", // 操作用户ID(关键字段)
"open_id": "ou_****",
"union_id": "on_****"
},
"moment_id": "mom_****", // 被操作的动态ID
"operator_id": {
"user_id": "ou_****",
"open_id": "ou_****",
"union_id": "on_****"
}
}
}
```
**关键字段说明**:
- `event.operator.user_id`:执行反应的**用户ID**(需提取)
- `header.token`:用于事件来源验证[^1]
- `event.moment_id`:关联的动态ID(可用于上下文扩展)
#### 2. **数据处理流程**
##### (1) 事件数据解密
飞书事件数据通过AES-256-CBC加密传输,解密步骤:
```python
import base64
from Crypto.Cipher import AES
def decrypt_event(encrypt_key: str, encrypted_data: str) -> dict:
# Base64解码
raw_data = base64.b64decode(encrypted_data)
# 提取初始向量(IV)和密文
iv = raw_data[:16]
ciphertext = raw_data[16:]
# AES解密
cipher = AES.new(encrypt_key.encode(), AES.MODE_CBC, iv)
decrypted = cipher.decrypt(ciphertext)
# 移除PKCS#7填充
unpadder = lambda s: s[:-s[-1]]
return json.loads(unpadder(decrypted))
```
> **参数说明**:
> - `encrypt_key`:开放平台配置的Encryption Key
> - `encrypted_data`:请求体中的`encrypt`字段值
##### (2) 用户ID提取
从解密后的JSON中直接提取:
```python
def extract_user_id(event_data: dict) -> str:
return event_data["event"]["operator"]["user_id"] # ou_**** 格式
```
#### 3. **调用用户信息API**
使用提取的`user_id`调用飞书[获取用户信息API](https://open.feishu.cn/document/server-docs/contact-v3/user/get):
```python
import requests
def get_user_info(access_token: str, user_id: str) -> dict:
url = f"https://open.feishu.cn/open-apis/contact/v3/users/{user_id}"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
response = requests.get(url, headers=headers)
return response.json()
```
**响应示例**:
```json
{
"data": {
"user": {
"user_id": "ou_****",
"name": "张三",
"mobile": "138****1234",
"department_ids": ["od_****"],
"open_id": "ou_****"
}
}
}
```
#### 4. **完整处理代码示例**
```python
# 配置参数
APP_ID = "cli_****"
APP_SECRET = "****"
ENCRYPT_KEY = "飞书后台Encryption Key"
def handle_event(request_data: dict):
# 1. 解密事件
decrypted = decrypt_event(
ENCRYPT_KEY,
request_data["encrypt"]
)
# 2. 验证事件Token(可选)
if decrypted["header"]["token"] != EXPECTED_TOKEN:
raise SecurityError("Token验证失败")
# 3. 提取用户ID
user_id = decrypted["event"]["operator"]["user_id"]
# 4. 获取API访问令牌
access_token = get_access_token(APP_ID, APP_SECRET)
# 5. 调用用户信息API
user_info = get_user_info(access_token, user_id)
# 6. 业务处理(如存储到数据库)
save_to_database(user_info)
```
#### 5. **关键注意事项**
1. **访问令牌管理**:
- 令牌有效期2小时,需实现缓存机制
- 获取令牌API:`POST https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/`
2. **事件安全验证**:
- 必须验证`header.token`与开放平台配置一致
- 推荐启用请求签名验证(`X-Lark-Signature`头)
3. **用户ID类型**:
- `user_id`(`ou_`开头)是租户内唯一标识
- 需使用`user_id`而非`open_id`调用用户API[^2]
4. **错误处理**:
- API调用返回码`code!=0`时需重试或记录
- 处理用户不存在等边界情况
> 飞书官方文档参考:
> - [事件订阅解密指南](https://open.feishu.cn/document/ukTMukTMukTM/uUTNz4SN1MjL1UzM)[^1]
> - [用户信息API文档](https://open.feishu.cn/document/server-docs/contact-v3/user/get)[^2]
阅读全文
相关推荐

















