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

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

4.2 KiB
Raw Permalink Blame History

飞书消息 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 编码

    $content = json_encode(['card' => $card]);
    
  2. 第二次编码:HttpClient::httpPost() 对整个数据再次编码

    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 标志:

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

修改前:

$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]
);

修改后:

$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

'content' => ['card' => $updatedCard],  // 直接传递数组

修复验证

JSON 编码测试结果

$ 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 能正确解析

示例对比

$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 修复状态: ✅ 完成