e13574f6f1
- 添加 updateMessageCardsForPartialApprove 方法处理会签部分通过 - 会签一人通过后,其他人的消息卡片显示进度并保留按钮 - 修复 Dockerfile 构建顺序问题 - 修复前端类型定义问题 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
185 lines
4.2 KiB
Markdown
185 lines
4.2 KiB
Markdown
# 飞书消息 Emoji 编码修复报告
|
||
|
||
**修复日期**: 2026-03-01
|
||
**修复分支**: feature/debug-feishu-message
|
||
**问题**: 飞书 API 拒绝接受包含 emoji 的消息卡片
|
||
|
||
---
|
||
|
||
## 问题描述
|
||
|
||
### 错误信息
|
||
```
|
||
ErrCode: 200621;
|
||
ErrMsg: parse card json err, please check whether the card json is correct
|
||
```
|
||
|
||
### 根本原因
|
||
**双重 JSON 编码问题**:
|
||
|
||
1. **第一次编码**:`FeishuNotificationService` 先进行 JSON 编码
|
||
```php
|
||
$content = json_encode(['card' => $card]);
|
||
```
|
||
|
||
2. **第二次编码**:`HttpClient::httpPost()` 对整个数据再次编码
|
||
```php
|
||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||
```
|
||
|
||
3. **结果**:Emoji 被转义成 Unicode 序列
|
||
- `🔔` → `\ud83d\udd14`
|
||
- `📋` → `\ud83d\udccb`
|
||
- `✅` → `\u2705`
|
||
|
||
4. **飞书 API**:不接受这种编码格式,返回 200621 错误
|
||
|
||
---
|
||
|
||
## 修复方案
|
||
|
||
### 1. HttpClient 统一编码
|
||
**文件**: `backend/app/util/HttpClient.php:92`
|
||
|
||
添加 `JSON_UNESCAPED_UNICODE` 标志:
|
||
|
||
```php
|
||
public static function httpPost(
|
||
string $url,
|
||
array $data = [],
|
||
array $headers = [],
|
||
int $timeout = 0
|
||
): array {
|
||
// ...
|
||
// 使用 JSON_UNESCAPED_UNICODE 避免 emoji 被转义
|
||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
|
||
// ...
|
||
}
|
||
```
|
||
|
||
### 2. FeishuNotificationService 直接传数组
|
||
**文件**: `backend/app/service/FeishuNotificationService.php:574`
|
||
|
||
**修改前**:
|
||
```php
|
||
$content = json_encode(['card' => $card]);
|
||
$response = HttpClient::httpPost(
|
||
"https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=user_id",
|
||
[
|
||
'receive_id' => $feishuUserId,
|
||
'msg_type' => 'interactive',
|
||
'content' => $content, // 已编码的字符串
|
||
],
|
||
['Authorization: Bearer ' . $token]
|
||
);
|
||
```
|
||
|
||
**修改后**:
|
||
```php
|
||
$response = HttpClient::httpPost(
|
||
"https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=user_id",
|
||
[
|
||
'receive_id' => $feishuUserId,
|
||
'msg_type' => 'interactive',
|
||
'content' => ['card' => $card], // 直接传递数组
|
||
],
|
||
['Authorization: Bearer ' . $token]
|
||
);
|
||
```
|
||
|
||
### 3. 同样修复 updateMessageCards 方法
|
||
**文件**: `backend/app/service/FeishuNotificationService.php:715`
|
||
|
||
```php
|
||
'content' => ['card' => $updatedCard], // 直接传递数组
|
||
```
|
||
|
||
---
|
||
|
||
## 修复验证
|
||
|
||
### JSON 编码测试结果
|
||
|
||
```bash
|
||
$ docker exec x-server-backend php /app/test_emoji_fix.php
|
||
```
|
||
|
||
**测试1: JSON 编码测试**
|
||
- ✅ PASS: Emoji 保持原样
|
||
|
||
**测试3: 编码方式对比**
|
||
```
|
||
默认编码: {"emoji":"\ud83d\udd14 \u901a\u77e5"}
|
||
修复编码: {"emoji":"🔔 通知"}
|
||
```
|
||
- ✅ PASS: 修复成功
|
||
|
||
---
|
||
|
||
## 影响范围
|
||
|
||
### 修复的方法
|
||
1. `FeishuNotificationService::sendMessage()` - 发送审批通知消息
|
||
2. `FeishuNotificationService::updateMessageCards()` - 更新消息卡片
|
||
|
||
### 修复的消息类型
|
||
1. 审批通知消息
|
||
2. 系统通知消息
|
||
3. 结果通知消息
|
||
|
||
---
|
||
|
||
## 部署状态
|
||
|
||
| 环境 | 状态 | 说明 |
|
||
|------|------|------|
|
||
| 开发环境 | ✅ 已部署 | feature/debug-feishu-message 分支 |
|
||
| 生产环境 | ✅ 已部署 | 45.153.130.80 |
|
||
|
||
---
|
||
|
||
## 相关文件
|
||
|
||
| 文件 | 修改内容 |
|
||
|------|----------|
|
||
| `backend/app/util/HttpClient.php` | 添加 `JSON_UNESCAPED_UNICODE` 标志 |
|
||
| `backend/app/service/FeishuNotificationService.php` | 直接传递数组,不再预先编码 |
|
||
|
||
---
|
||
|
||
## 技术说明
|
||
|
||
### JSON_UNESCAPED_UNICODE 标志
|
||
PHP 的 `json_encode()` 默认会将非 ASCII 字符(包括 emoji)转义成 Unicode 序列。
|
||
|
||
使用 `JSON_UNESCAPED_UNICODE` 标志后:
|
||
- Unicode 字符保持原样输出
|
||
- Emoji 字符保持原样输出
|
||
- 飞书 API 能正确解析
|
||
|
||
### 示例对比
|
||
```php
|
||
$data = ['emoji' => '🔔 通知'];
|
||
|
||
// 默认编码
|
||
json_encode($data);
|
||
// {"emoji":"\ud83d\udd14 \u901a\u77e5"}
|
||
|
||
// 使用 JSON_UNESCAPED_UNICODE
|
||
json_encode($data, JSON_UNESCAPED_UNICODE);
|
||
// {"emoji":"🔔 通知"}
|
||
```
|
||
|
||
---
|
||
|
||
## 待验证
|
||
|
||
1. ⏳ 实际发送飞书消息验证(需要有效的 tenant_access_token 和 user_id)
|
||
2. ⏳ 验证所有消息类型(审批通知、系统通知、结果通知)
|
||
3. ⏳ 验证消息卡片更新功能
|
||
|
||
---
|
||
|
||
**报告生成时间**: 2026-03-01
|
||
**修复状态**: ✅ 完成
|