# 错误与状态规范 ## 1. HTTP 返回的含义 | HTTP 状态 | 含义 | 业务项目处理 | | --- | --- | --- | | `200` | 相同幂等请求已存在,返回原记录 | 按成功受理处理,不再次生成新幂等键 | | `202` | 新通知已进入队列 | 保存 `id`,不要当作已经送达 | | `401` | 项目、时间戳或签名认证失败 | 停止重试,检查配置、时钟和签名实现 | | `404` | 查询的投递记录不存在或不属于当前项目 | 检查投递 ID,不能跨项目查询 | | `409` | nonce 重放或幂等内容冲突 | nonce 重放可用新 nonce 重发;幂等冲突必须修正业务逻辑 | | `422` | 消息字段、标题、长度或跳转地址不合法 | 修正请求内容后再提交 | | `500` | 通知中心内部异常 | 保留原幂等键,短暂退避后有限重试并告警 | ## 2. 投递状态 | 状态 | 含义 | 是否终态 | | --- | --- | --- | | `pending` | 已受理,等待发送 | 否 | | `sending` | 工作进程正在投递 | 否 | | `retrying` | 明确未受理,等待通知中心自动重试 | 否 | | `sent` | 钉钉已接受发送请求,等待读取结果 | 否 | | `unread` | 已投递但收件人尚未阅读 | 否 | | `read` | 收件人已经阅读 | 是 | | `failed` | 已确定失败或投递结果不确定且为避免重复而隔离 | 是 | 业务项目一般不需要自己重发 `pending / sending / retrying / sent / unread` 状态。通知中心会处理明确可重试的失败并查询已读状态。 ## 3. 标准边界 - 业务项目必须按统一请求契约提交 `recipientUserId` 或 `recipientAlias`、标题、正文和幂等键;`actionUrl` 可空且非空时原样透传。通知中心不识别采购流程、供应商账号、邮箱等项目私有字段。 - 收件人解析和业务人员配置由接入方负责。姓名、邮箱、外部联系人 ID 和自行拼接的标识不能代替钉钉内部员工 `userId`。 - 接入方以 HTTP 状态和投递 `status` 作为自动控制流程依据;`lastErrorCode` 用于诊断和人工处置,不得根据未知错误码自行重发。 - 本规范公布的统一 `lastErrorCode` 语义稳定;尚未归一化的钉钉厂商错误码或附在 `lastErrorMessage` 中的厂商信息只供通知中心维护人排查,不构成对接契约。 ## 4. 常见错误码 | 错误码 | 原因 | 处理方式 | | --- | --- | --- | | `AUTH_REQUIRED` | 项目不存在或签名请求头缺失 | 检查项目配置和请求头 | | `TIMESTAMP_EXPIRED` | 客户端与服务器时间相差超过五分钟 | 校准系统时间后使用新 nonce 重发 | | `NONCE_INVALID` | nonce 格式不符合要求 | 使用 16~100 位随机 URL 安全字符串 | | `SIGNATURE_INVALID` | 签名路径、正文或密钥不一致 | 使用实际 raw body 和内部 API path 重新计算 | | `NONCE_REPLAYED` | nonce 已经使用过 | 生成新 nonce,业务幂等键保持不变 | | `RECIPIENT_SELECTOR_CONFLICT` | 同时提交了 `recipientUserId` 和 `recipientAlias` | 两种收件人方式只保留一个 | | `RECIPIENT_ALIAS_INVALID` | 花名为空、过长或包含控制字符 | 提交不超过 50 字符的准确花名 | | `RECIPIENT_ALIAS_NOT_FOUND` | 素材平台内部目录没有找到该花名对应的钉钉 `userId` | 核对准确花名或改用钉钉 `userId`,不得猜测重试 | | `RECIPIENT_ALIAS_DIRECTORY_UNAVAILABLE` | 素材平台内部目录暂时不可用 | 不入队,稍后重试或联系维护人 | | `ACTION_URL_INVALID` | `actionUrl` 过长或包含非法控制字符 | 修正链接后重试 | | `IDEMPOTENCY_KEY_INVALID` | 幂等键为空或过长 | 按统一格式生成稳定键 | | `MESSAGE_FIELDS_REQUIRED` | 收件人、标题或内容缺失 | 补齐字段 | | `MESSAGE_TOO_LONG` | 标题、正文或按钮超长 | 压缩内容,把详情放回业务页面 | | `TITLE_BRAND_RESERVED` | 业务标题自行添加了“由壹点”品牌前缀 | 只提交业务标题 | | `IDEMPOTENCY_CONFLICT` | 同一幂等键对应了不同内容 | 修复幂等键或业务数据,不得随机换键规避 | | `NOT_FOUND` | 记录不存在或跨项目访问 | 检查投递 ID 和项目身份 | | `INTERNAL_ERROR` | 通知中心内部异常 | 原幂等键退避重试并通知维护人员 | | `FLOW_CONTROLLED` | 钉钉明确拒绝本次请求并要求限流 | 接入方等待,通知中心内部自动有限重试 | | `INVALID_USER` | 钉钉确认收件人内部员工 `userId` 无效或不存在 | 接入方修正人员数据,不得改用邮箱或姓名重试 | | `FILTERED_USER` | 收件人不在统一机器人可见范围内 | 由通知中心维护人核对应用权限范围 | | `DELIVERY_OUTCOME_UNKNOWN` | 请求可能已经送达,无法安全确认 | 停止自动重试,人工核验后决定是否补发 | 投递进入 `failed` 时,还应记录返回中的 `lastErrorCode` 和 `lastErrorMessage`,但不要直接把内部错误原文展示给最终用户。 ## 5. 业务端重试原则 1. 连接失败或 HTTP `500` 时可以有限重试,必须复用原 `idempotencyKey`,每次生成新 nonce。 2. HTTP `200 / 202` 后不要重新创建通知,改为查询返回的投递 ID。 3. HTTP `401 / 409 / 422` 默认不自动重试;先修正配置或请求。 4. 通知中心内部会处理明确未受理的钉钉限流等情况,业务项目不得再叠加无限重试。 5. `DELIVERY_OUTCOME_UNKNOWN` 表示发送结果无法确定。为避免用户收到重复单聊,通知中心会停止自动重试;业务负责人确认后才能人工补发。 ## 6. 建议监控 业务项目至少记录: - 业务事件 ID - `idempotencyKey` - 通知中心投递 `id` - 收件人钉钉 `userId` - 最近一次查询状态 - `lastErrorCode` 禁止记录项目密钥、签名原文中的密钥、钉钉 AppSecret、免登码和一次性登录票据。