Files
x-server-mobile/docs/EMOJI_FIX_REPORT_20260301.md
T
root e13574f6f1 fix: 修复会签场景下消息卡片更新问题
- 添加 updateMessageCardsForPartialApprove 方法处理会签部分通过
- 会签一人通过后,其他人的消息卡片显示进度并保留按钮
- 修复 Dockerfile 构建顺序问题
- 修复前端类型定义问题

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 10:33:40 +08:00

185 lines
4.2 KiB
Markdown
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.
# 飞书消息 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
**修复状态**: ✅ 完成