接入概览
开放平台使用 API Key + HMAC-SHA256 签名鉴权。请求必须携带标准请求头,部分接口还要求通过 X-Employee-Virtual-Id 明确当前访问员工。
基础规则
- 所有接口根路径为
/api/openplatform。 - 所有返回体统一为
statusCode、msg、data结构。 - 接口只作用于当前 API Key 所属企业,不支持跨企业访问。
- 权限项由后端统一返回并在请求时强校验,空权限不会默认放行。
统一员工识别规则
/employees会为当前 API Key 下的每个企业成员实时返回唯一且稳定的virtualId,不需要预先分配或额外落库。- 需要识别“当前访问用户/操作人/创建人”的接口,统一通过
X-Employee-Virtual-Id传当前员工的virtualId。 - 如果接口要求当前员工身份但未传该请求头,服务端会直接返回
403。 - 绑定接口用于建立第三方账号与当前 API Key 下员工
virtualId的映射关系。
鉴权签名
每次请求都必须携带以下请求头:
| 请求头 | 必填 | 说明 |
|---|---|---|
X-API-Key |
是 | 企业后台 API Key 管理中生成的 Key。 |
X-Timestamp |
是 | Unix 秒级时间戳,允许与服务端有 5 分钟内时钟偏差。 |
X-Nonce |
是 | 每次请求唯一随机串,用于防重放。推荐直接使用 UUID/GUID 等高强度随机值,禁止使用固定前缀 + 秒级时间戳这类易重复方案。 |
X-Signature |
是 | 按本文档规范拼接原文后,用 API Secret 做 HMAC-SHA256,结果转 16 进制小写。 |
X-Employee-Virtual-Id |
按接口要求 | 当前访问员工的 virtualId。只有需要识别当前员工身份的接口才必须传。 |
签名原文格式
METHOD PATH_LOWERCASE QUERY_STRING API_KEY TIMESTAMP NONCE EMPLOYEE_VIRTUAL_ID BODY
METHOD必须转为大写,例如GET、POST。PATH必须使用实际请求路径,并统一转为小写,例如/api/openplatform/employees。QUERY_STRING必须与实际请求 URL 完全一致;有查询串时必须包含前导?,没有时传空字符串。重要:如果查询参数包含中文或特殊字符,必须先进行 URL 编码(例如Uri.EscapeDataString或encodeURIComponent),签名原文中的查询串必须与实际发送的 URL 中的查询串完全一致。- 如果本次请求带了
X-Employee-Virtual-Id,则EMPLOYEE_VIRTUAL_ID必须传该请求头的原始值;没有该请求头时传空字符串。 BODY必须与实际发送的请求体完全一致;GET 请求 body 为空字符串;multipart/form-data上传附件时固定传[multipart/form-data]。- 签名原文内部统一使用
\n作为换行符,不使用\r\n。 X-Nonce必须为每次 HTTP 请求重新生成的唯一随机串;同一个 API Key 下,已通过鉴权的X-Nonce不可再次使用。- 如果客户端需要重试请求,必须同时重新生成
X-Timestamp、X-Nonce、X-Signature,禁止复用上一次请求头。
- 开放平台会按
API Key + X-Nonce记录已通过鉴权的请求,防止请求被重复使用。 - 同一个
API Key下,只要请求仍处于 5 分钟有效时间窗内,重复使用同一个X-Nonce就会被判定为重放请求。 - 无论业务处理最终成功还是失败,只要客户端再次发起 HTTP 请求,都必须视为一次全新的签名请求。
- 如果重复使用同一个
X-Nonce,服务端会直接返回401,错误信息为“请求已被重复使用”。 X-Nonce只用于请求防重放,不是业务幂等键。若三方需要“安全重试但不能重复创建业务数据”,请在业务参数里自行携带独立的幂等标识。
- 开放平台网关会尽量兼容三方常见的弱类型 JSON 传参,例如把数字传成数字字符串、把布尔值传成
"true"/"false"或"1"/"0"。 - 可选字段没有值时,推荐直接省略字段,或显式传
null;不要依赖空字符串""表示“空”。当前仅对少量高频场景做了兼容,非法值仍会按参数错误处理。 - 为保证联调稳定,三方仍应优先按字段声明的标准 JSON 类型传值,不要把所有字段统一转成字符串。
签名示例(GET - 无查询参数)
GET /api/openplatform/labels sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 1718515200 f4f7ec0b3d574c27a5d1c32f5d4d6e90 emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz
签名示例(GET - 带中文查询参数)
GET /api/openplatform/employees ?page=1&pageSize=100&keyword=%E5%BC%A0%E6%96%87%E6%B6%9B sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 1718515200 f4f7ec0b3d574c27a5d1c32f5d4d6e90 emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz
- 如果查询参数包含中文、空格、特殊符号(如
&、=),必须先进行 URL 编码,再参与签名计算。 - C# 使用
Uri.EscapeDataString("张文涛"),编码结果为%E5%BC%A0%E6%96%87%E6%B6%9B。 - JavaScript 使用
encodeURIComponent("张文涛"),编码结果相同。 - 签名原文中的查询串必须与实际发送的 URL 中的查询串完全一致,否则服务端会返回 401 签名校验失败。
- RestSharp 的
AddQueryParameter方法会自动进行 URL 编码,签名时应使用编码后的查询串。
签名示例(POST + JSON Body)
POST
/api/openplatform/tasks
sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
1718515200
f4f7ec0b3d574c27a5d1c32f5d4d6e90
emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz
{"title":"完成季度复盘","assigneeVirtualIds":["emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz"],"closingTime":"2026-06-18T00:00:00+08:00"}签名示例(POST + multipart/form-data)
POST /api/openplatform/attachments/upload sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 1718515200 f4f7ec0b3d574c27a5d1c32f5d4d6e90 emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz [multipart/form-data]
- GET 请求签名时漏掉
?前缀。 - 签名使用的 QueryString 参数顺序与实际 URL 不一致。
- 查询参数包含中文但未进行 URL 编码,导致签名原文与实际发送的 URL 不一致。
- 使用 HTTP 客户端库(如 RestSharp、axios)时,客户端自动编码了查询参数,但签名时使用了未编码的原始值。
- 请求头里传了
X-Employee-Virtual-Id,但签名原文里的EMPLOYEE_VIRTUAL_ID仍然留空,或值不一致。 - 附件上传签名时误把二进制文件正文参与签名,而不是固定占位
[multipart/form-data]。 - POST 请求签名时 JSON 被格式化过,而实际发送时又重新序列化了一次。
- 签名时使用了毫秒级时间戳,或者用了大写十六进制签名串。
- 请求失败后直接复用上一次的
X-Timestamp、X-Nonce、X-Signature重试,导致被判定为重复请求。 - 客户端并发发送多个请求时错误复用了同一个
X-Nonce。
权限说明
当前 API Key 的权限配置只接受字符串数组 JSON,例如 ["employees.read","tasks.write"]。对象结构或空权限都不会被放行。
| 权限值 | 名称 | 说明 |
|---|---|---|
bindings.read |
读取绑定关系 | 允许查询当前 API Key 下的第三方账号绑定列表和绑定详情。 |
bindings.write |
管理绑定关系 | 允许创建、更新和解绑当前 API Key 下的第三方账号绑定关系。 |
employees.read |
读取员工 | 允许查询当前企业员工列表和员工详情。 |
labels.read |
读取标签 | 允许按当前访问员工身份查询当前企业可见标签。 |
tasks.read |
读取任务 | 允许查询当前企业任务列表和任务详情。 |
tasks.write |
创建任务 | 允许在当前企业创建任务。 |
scores.read |
读取贡献点 | 允许查询员工当前贡献点信息。 |
scores.write |
变更贡献点 | 允许提交贡献点增加或扣减请求。 |
scoreApplies.read |
读取贡献申请 | 允许查询贡献申请列表和详情。 |
scoreApplies.write |
提交贡献申请 | 允许提交贡献申请。 |
attachments.write |
上传附件 | 允许上传任务附件。 |
员工接口
员工接口用于查询当前企业员工及其开放平台映射信息。返回手机号已脱敏。
查询当前企业员工分页列表。
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
page | int | 否 | 页码,默认 1。 |
pageSize | int | 否 | 每页条数,默认 20,最大 100。 |
keyword | string | 否 | 按员工姓名或拼音模糊查询。 |
响应示例
{
"statusCode": 100,
"msg": "获取成功",
"data": {
"total": 1,
"rows": [
{
"virtualId": "emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz",
"name": "张三",
"phone": "138****5678",
"departmentName": "运营部",
"positionName": "运营经理",
"isBound": true
}
]
}
}virtualId 在当前企业 + 当前 API Key 维度唯一且稳定,由平台按算法实时生成;重置 ApiSecret 不会改变同一员工在当前 API Key 下的 virtualId。isBound 表示当前 API Key 下是否已存在第三方账号绑定关系。按 virtualId 查询员工详情。
响应示例
{
"statusCode": 100,
"msg": "获取成功",
"data": {
"virtualId": "emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz",
"name": "张三",
"phone": "138****5678",
"departmentName": "运营部",
"positionName": "运营经理",
"isBound": true
}
}绑定接口
绑定接口是正式公开 API,用于维护第三方账号与当前 API Key 下员工 virtualId 的映射关系。
查询当前 API Key 下的绑定列表。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
page | int | 否 | 页码,默认 1。 |
pageSize | int | 否 | 每页条数,默认 20,最大 100。 |
{
"statusCode": 100,
"msg": "获取成功",
"data": {
"total": 1,
"rows": [
{
"id": "7e6c92f1-6a34-4f5c-8bcf-faf6db5132a8",
"thirdPartyUserId": "u_10001",
"thirdPartyUserName": "张三-外部账号",
"employeeVirtualId": "emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz",
"createTime": "2026-06-17T16:00:00+08:00",
"updateTime": "2026-06-17T16:00:00+08:00"
}
]
}
}查询单个绑定详情,返回结构与绑定列表中的单条记录一致。
新增或更新绑定关系。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
thirdPartyUserId | string | 是 | 第三方系统用户唯一标识。 |
thirdPartyUserName | string | 否 | 第三方系统用户名。 |
employeeVirtualId | string | 是 | 当前 API Key 下员工的 virtualId。 |
{
"thirdPartyUserId": "u_10001",
"thirdPartyUserName": "张三-外部账号",
"employeeVirtualId": "emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz"
}thirdPartyUserName 为空时建议直接省略或传 null;必填字段 thirdPartyUserId、employeeVirtualId 仍必须传有效字符串。按第三方用户 ID 解绑。
{
"statusCode": 100,
"msg": "解绑成功",
"data": null
}任务接口
任务接口支持列表、详情和创建。创建任务时必须通过 X-Employee-Virtual-Id 统一指定当前创建人。
X-Employee-Virtual-Id,会直接返回 403,错误信息为“请通过 X-Employee-Virtual-Id 指定当前企业下的创建人”。查询任务列表。
X-Employee-Virtual-Id。未传时返回 403,错误信息为"请通过 X-Employee-Virtual-Id 指定当前访问用户"。| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
page | int | 否 | 页码,默认 1。 |
pageSize | int | 否 | 每页条数,默认 20,最大 100。 |
status | int | 否 | 任务状态过滤。 |
keyword | string | 否 | 任务标题关键字。 |
{
"statusCode": 100,
"msg": "获取成功",
"data": {
"total": 1,
"rows": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "完成季度复盘",
"assigneeVirtualId": "emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz",
"dueDate": "2026-06-19T00:00:00+08:00",
"priority": 3,
"status": 2,
"createTime": "2026-06-17T18:40:00+08:00"
}
]
}
}查询任务详情。
X-Employee-Virtual-Id。未传时返回 403,错误信息为"请通过 X-Employee-Virtual-Id 指定当前访问用户"。{
"statusCode": 100,
"msg": "获取成功",
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "完成季度复盘",
"contents": "整理经营数据并输出总结
",
"assigneeVirtualId": "emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz",
"dueDate": "2026-06-18T00:00:00+08:00",
"priority": 3,
"status": 2,
"createTime": "2026-06-17T16:00:00+08:00",
"apiKeyName": "我的三方应用"
}
}apiKeyName 表示创建该任务的三方应用名称,非开放平台创建的任务该字段为空字符串。创建任务。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
title | string | 是 | 任务标题。 |
contents | string | 否 | 编码后的富文本内容。请按 URL 编码口径提交;开放平台接收后会先做 URL 解码,再做 HTML 解码,最终按原始富文本存储。 |
assigneeVirtualIds | string[] | 条件必填 | 负责人 virtualId 列表。正式发送任务时至少 1 个;保存草稿时可不传。 |
checkerVirtualId | string | 否 | 考核人 virtualId。 |
ccVirtualIds | string[] | 否 | 抄送人 virtualId 列表。 |
priority | int | 否 | 优先级,默认 3。 |
closingTime | string | 条件必填 | 截止时间。正式发送任务时必填;保存草稿时可不传。传值时必须显式带时区信息,例如 2026-06-18T00:00:00+08:00 或 2026-06-17T16:00:00Z。 |
score | decimal | 否 | 关联贡献点,单位为“点”。 |
labelIdArr | guid[] | 否 | 标签 ID 列表。 |
planId | guid | 否 | 所属项目 ID。不传时请直接省略该字段,或显式传 null;不要传空字符串 ""。 |
isDraft | bool | 否 | 是否保存草稿。 |
sendTime | string | 否 | 发送时间,ISO 8601 标准时间字符串,且必须显式带时区信息,例如 2026-06-17T19:00:00+08:00 或 2026-06-17T11:00:00Z。 |
completeAttachment | bool | 否 | 完成任务时是否必须上传附件。 |
checkItems | string[] | 否 | 检查项列表。 |
attachmentUrls | string[] | 否 | 已上传附件 URL 列表。 |
{
"title": "完成季度复盘",
"contents": "%3Cp%3E%E6%95%B4%E7%90%86%E7%BB%8F%E8%90%A5%E6%95%B0%E6%8D%AE%E5%B9%B6%E8%BE%93%E5%87%BA%3Cstrong%3E%E6%80%BB%E7%BB%93%3C%2Fstrong%3E%3C%2Fp%3E",
"assigneeVirtualIds": ["emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz"],
"priority": 3,
"closingTime": "2026-06-18T00:00:00+08:00",
"score": 5
}contents 必须传已做 URL 编码的富文本字符串,例如先把 <p>...</p> 执行 encodeURIComponent 后再提交。开放平台会先做 URL 解码,再补做 HTML 解码,最终按富文本原文存储。guid 字段(如 planId)如果没有值,请直接省略或传 null;不要传空字符串 ""。closingTime、sendTime 传值时必须使用带时区信息的 ISO 8601 标准时间字符串,例如 2026-06-18T00:00:00+08:00 或 2026-06-17T16:00:00Z;不接受没有时区的裸时间字符串。score、isDraft、completeAttachment 建议按标准 JSON 数字/布尔值传递,不要全部转成字符串。isDraft=true 时,当前开放平台只强制要求 title;assigneeVirtualIds、closingTime、checkerVirtualId、ccVirtualIds、score、labelIdArr、planId、sendTime、completeAttachment、checkItems、attachmentUrls 都可省略。正式发送任务时,仍至少需要负责人和截止时间。{
"statusCode": 100,
"msg": "创建成功",
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "完成季度复盘",
"createTime": "2026-06-17T16:00:00+08:00"
}
}score 支持按数字提交,也兼容数字字符串;但仍建议三方按标准 JSON 数字传值,避免不同语言序列化差异。撤销任务。只有任务创建人才能撤销,且任务状态必须为进行中、待开始、暂停、待审批、草稿、拒收、新任务、审批不通过、转发中、挂起申请之一。
X-Employee-Virtual-Id。未传时返回 403。DELETE /api/openplatform/tasks/a1b2c3d4-e5f6-7890-abcd-ef1234567890 Host: your-domain.com Content-Type: application/json X-Employee-Virtual-Id: emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz
路径参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | guid | 是 | 任务 ID。 |
{
"statusCode": 100,
"msg": "撤销成功"
}task.canceled Webhook 事件。Webhook 事件通知
开放平台支持通过 Webhook 向三方应用主动推送任务事件通知。在 API Key 管理中配置回调地址和订阅的事件类型后,当相关任务发生状态变更时,开放平台会异步发送 HTTP POST 请求到指定的回调地址。
事件类型
| 事件类型 | 分类 | 子分类 | 触发时机 |
|---|---|---|---|
task.created | 任务类 | 创建与发送 | 任务创建成功(含草稿)。 |
task.sent | 任务类 | 创建与发送 | 任务发送。 |
task.awaitStart | 任务类 | 创建与发送 | 任务待开始。 |
task.received | 任务类 | 执行流程 | 任务接收。 |
task.refused | 任务类 | 执行流程 | 任务被拒收。 |
task.executing | 任务类 | 执行流程 | 任务执行中。 |
task.submitted | 任务类 | 执行流程 | 任务提交完成。 |
task.completed | 任务类 | 执行流程 | 任务已完成。 |
task.audited | 任务类 | 审核与审批 | 完成审核通过。 |
task.auditRejected | 任务类 | 审核与审批 | 完成审核不通过。 |
task.awaitingApproval | 任务类 | 审核与审批 | 待审批。 |
task.approved | 任务类 | 审核与审批 | 审批通过。 |
task.approvalRejected | 任务类 | 审核与审批 | 审批不通过。 |
task.changed | 任务类 | 审核与审批 | 任务变更待确认。 |
task.paused | 任务类 | 挂起与暂停 | 任务已暂停。 |
task.hangApply | 任务类 | 挂起与暂停 | 挂起申请。 |
task.hangFiringApply | 任务类 | 挂起与暂停 | 申请挂起启动。 |
task.suspended | 任务类 | 挂起与暂停 | 任务已挂起。 |
task.forwarding | 任务类 | 转发与申诉 | 转发中。 |
task.relayRefused | 任务类 | 转发与申诉 | 转发拒收。 |
task.relayAwaitingApproval | 任务类 | 转发与申诉 | 转发任务待审批。 |
task.relayApprovalRejected | 任务类 | 转发与申诉 | 转发任务审批不通过。 |
task.rejected | 任务类 | 特殊流程 | 完成任务被申诉。 |
task.canceled | 任务类 | 特殊流程 | 任务撤销申请。 |
task.deleted | 任务类 | 删除 | 任务删除。 |
score_apply.created | 贡献类 | - | 贡献申请创建成功。 |
score_apply.approved | 贡献类 | - | 贡献申请审批通过。 |
score_apply.rejected | 贡献类 | - | 贡献申请审批不通过。 |
score_apply.complained | 贡献类 | - | 贡献申请被申诉。 |
score_apply.confirmed | 贡献类 | - | 贡献申请被确认。 |
common.accountUnbound | 通用类 | - | 三方账号解绑。 |
查询支持的事件类型
三方应用可以通过接口动态获取开放平台支持的全部事件类型,用于构建事件订阅 UI 或验证配置:
权限要求:需要有效的 API Key 鉴权(X-API-Key、X-Timestamp、X-Nonce、X-Signature)。
请求示例
GET /api/openplatform/tasks/webhook-event-types HTTP/1.1 Host: your-openplatform-domain.com X-API-Key: your-api-key-here X-Timestamp: 1718620800 X-Nonce: abc123def456 X-Signature: hmac-sha256-signature-here
响应示例
{
"statusCode": 100,
"msg": "获取成功",
"data": [
{
"value": "task.created",
"label": "任务创建",
"description": "通过开放平台 API 创建任务时触发。",
"category": "task"
},
{
"value": "task.sent",
"label": "任务下发",
"description": "任务下发给执行人时触发。",
"category": "task"
},
{
"value": "task.received",
"label": "任务接收",
"description": "执行人接收任务时触发。",
"category": "task"
},
{
"value": "task.executing",
"label": "任务开始执行",
"description": "执行人开始执行任务时触发。",
"category": "task"
},
{
"value": "task.paused",
"label": "任务暂停",
"description": "任务被暂停时触发。",
"category": "task"
},
{
"value": "task.suspended",
"label": "任务挂起",
"description": "任务被挂起时触发。",
"category": "task"
},
{
"value": "task.deleted",
"label": "任务删除",
"description": "任务被删除时触发。",
"category": "task"
},
{
"value": "task.submitted",
"label": "任务提交",
"description": "执行人完成任务并提交时触发。",
"category": "task"
},
{
"value": "task.approved",
"label": "任务审核通过",
"description": "任务审核人通过任务时触发。",
"category": "task"
},
{
"value": "task.rejected",
"label": "任务审核不通过",
"description": "任务审核人不通过任务时触发。",
"category": "task"
},
{
"value": "score_apply.created",
"label": "贡献申请创建成功",
"description": "通过开放平台提交贡献申请成功后触发。",
"category": "score"
},
{
"value": "score_apply.approved",
"label": "贡献申请审批通过",
"description": "贡献申请审批通过时触发。",
"category": "score"
},
{
"value": "score_apply.rejected",
"label": "贡献申请审批不通过",
"description": "贡献申请审批不通过时触发。",
"category": "score"
},
{
"value": "score_apply.complained",
"label": "贡献申请申诉",
"description": "贡献申请被申诉时触发。",
"category": "score"
},
{
"value": "score_apply.confirmed",
"label": "贡献申请确认",
"description": "贡献申请被确认时触发。",
"category": "score"
},
{
"value": "common.accountUnbound",
"label": "账号解绑",
"description": "三方账号与员工解绑时触发。",
"category": "common"
}
]
}响应字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
statusCode | int | 状态码,100 表示成功。 |
msg | string | 响应消息。 |
data | array | 事件类型列表。 |
data[].value | string | 事件类型标识符,用于订阅配置。 |
data[].label | string | 事件类型的中文名称。 |
data[].description | string | 事件触发时机的详细说明。 |
data[].category | string | 事件分类:task(任务类)、score(贡献类)或 common(通用类)。 |
事件 Data 字段详解
不同事件类型会在 data 字段中返回不同的业务数据,三方应用应根据 eventType 解析对应字段:
1. 任务类事件
task.created - 任务创建
| 字段 | 类型 | 说明 |
|---|---|---|
taskId | string (UUID) | 任务唯一标识符。 |
title | string | 任务标题。 |
createTime | string | 任务创建时间(ISO 8601 格式)。 |
{
"eventType": "task.created",
"timestamp": "1718620800",
"data": {
"taskId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "完成季度复盘",
"createTime": "2026-06-17T16:00:00"
}
}task.sent / task.received / task.executing / task.paused / task.suspended / task.deleted / task.submitted / task.approved / task.rejected - 任务状态变更
| 字段 | 类型 | 说明 |
|---|---|---|
taskId | string (UUID) | 任务唯一标识符。 |
eventType | string | 事件类型标识,与外层 eventType 一致。 |
GET /api/openplatform/tasks/{taskId} 接口获取任务完整详情(包括状态、执行人、进度、附件等)。{
"eventType": "task.submitted",
"timestamp": "1718624400",
"data": {
"taskId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"eventType": "task.submitted"
}
}2. 贡献类事件
score_apply.created - 贡献申请创建
| 字段 | 类型 | 说明 |
|---|---|---|
applyId | string (UUID) | 贡献申请唯一标识符。 |
applyType | int | 申请类型(具体类型值需参考贡献模块文档)。 |
score | decimal | 申请贡献点数量。 |
isAnonymous | bool | 是否匿名申请。 |
createTime | string | 申请创建时间(ISO 8601 格式)。 |
{
"eventType": "score_apply.created",
"timestamp": "1718628000",
"data": {
"applyId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"applyType": 1,
"score": 5.0,
"isAnonymous": false,
"createTime": "2026-06-17T17:00:00"
}
}score_apply.approved / score_apply.rejected / score_apply.confirmed / score_apply.complained - 贡献申请状态变更
| 字段 | 类型 | 说明 |
|---|---|---|
applyId | string (UUID) | 贡献申请唯一标识符。 |
statusCode | int | 业务状态码(100 表示成功)。 |
msg | string | 状态变更说明信息(如审批意见)。 |
{
"eventType": "score_apply.approved",
"timestamp": "1718631600",
"data": {
"applyId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"statusCode": 100,
"msg": "审批通过"
}
}score_apply.rejected、score_apply.confirmed、score_apply.complained 的 data 字段结构与上例一致,均返回 applyId、statusCode、msg。3. 通用类事件
common.accountUnbound - 三方账号解绑
| 字段 | 类型 | 说明 |
|---|---|---|
apiKeyId | string (UUID) | API Key 唯一标识符。 |
thirdPartyUserId | string | 三方系统的用户唯一标识。 |
employeeVirtualId | string | 慧企星助平台员工虚拟 ID(解绑后该员工将无法再通过三方身份访问)。 |
eventType | string | 事件类型标识,固定为 "common.accountUnbound"。 |
- 立即失效该
thirdPartyUserId对应的访问令牌或会话。 - 清除三方系统中与该员工相关的缓存数据。
- 如三方系统需要,可记录解绑时间用于审计。
- 该员工下次尝试通过三方登录时,应引导其重新授权绑定。
{
"eventType": "common.accountUnbound",
"timestamp": "1718635200",
"data": {
"apiKeyId": "c3d4e5f6-a7b8-9012-cdef-a12345678901",
"thirdPartyUserId": "third_party_user_12345",
"employeeVirtualId": "emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz",
"eventType": "common.accountUnbound"
}
}回调请求格式
开放平台会以 HTTP POST 方式向配置的回调地址发送 JSON 数据:
| 参数 | 类型 | 说明 |
|---|---|---|
eventType | string | 事件类型,如 task.created。 |
timestamp | string | Webhook 发送时的 Unix 秒级时间戳字符串,例如 "1718620800";该值同时用于签名验证与防重放校验。 |
data | object | 事件详细数据(由具体事件类型决定,见上方"事件 Data 字段详解")。 |
签名验证
每个 Webhook 请求都会在 HTTP Header 中携带签名,三方应用应验证签名以确保请求来源可信:
| Header 名称 | 说明 |
|---|---|
X-Webhook-Signature | HMAC-SHA256 签名值(十六进制字符串)。 |
X-Webhook-Timestamp | 与请求体中的 timestamp 一致,值为 Unix 秒级时间戳字符串。 |
签名算法:
signature = HMAC-SHA256( key = webhookSecret, message = timestamp + "\n" + requestBodyJson )
- 签名原文只包含
timestamp、换行符\n和完整请求体 JSON,不拼接eventType、URL 或其他 Header。 - 验签时必须使用接收到的原始请求体字符串,不能先反序列化再重新序列化,否则字段顺序、空格或转义差异会导致签名不一致。
- 建议按 UTC 校验时间戳有效期,例如与
DateTime.UtcNow比较,并控制在 5 分钟误差窗口内。
webhookSecret 由后端在配置 Webhook 回调地址时自动生成(256 位随机字符串,使用 RNGCryptoServiceProvider 密码学安全随机数生成器),用户可随时点击"重新生成密钥"按钮重新生成。密钥仅在配置成功时返回给前端展示,不会在网络传输中暴露。接收到 Webhook 请求后,务必先验证签名,再处理业务逻辑。重试机制
开放平台会对 Webhook 投递失败进行自动重试,最多重试 3 次:
- 第 1 次重试:失败后 1 分钟
- 第 2 次重试:失败后 5 分钟
- 第 3 次重试:失败后 30 分钟
如果 3 次重试后仍然失败,该事件将标记为投递失败,可在日志中查看详细信息。
{"statusCode":100,"msg":"success"};若验签失败、参数非法或业务未接收成功,应返回非成功 HTTP 状态码,或在响应体中返回非 100 的 statusCode,开放平台会按失败处理并进入重试。标签接口
标签接口会按当前访问员工身份返回可见标签列表,因此必须传 X-Employee-Virtual-Id。
获取当前企业可用标签列表。
X-Employee-Virtual-Id。未传时返回 403。{
"statusCode": 100,
"msg": "获取成功",
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "紧急",
"color": "#ff4d4f",
"groupName": "优先级"
},
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "我的标签",
"color": "#1890ff",
"groupName": null
}
]
}贡献点接口
贡献点接口的单位统一为“点”。增加、扣减贡献点时都必须通过 X-Employee-Virtual-Id 指定当前操作人。
增加贡献点。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
employeeVirtualId | string | 是 | 目标员工 virtualId。 |
score | decimal | 是 | 增加的贡献点数量,传正数。 |
reason | string | 是 | 变更原因。 |
typeId | string | 是 | 业务类型 ID。 |
{
"statusCode": 100,
"msg": "贡献变更请求已提交",
"data": {
"queueId": "6fbb6f6d-4fa0-4fd3-a87c-fdf691c2a260",
"employeeVirtualId": "emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz",
"score": 5,
"reason": "季度激励",
"acceptedTime": "2026-06-17T16:00:00+08:00"
}
}扣减贡献点。score 传正数即可,服务端会按扣减语义处理,返回结构与增加贡献点一致。
查询员工贡献点。
{
"statusCode": 100,
"msg": "获取成功",
"data": {
"employeeVirtualId": "emp_T7dQ2mYk8rLp4cN1sVx3bH9wAe6fJuKz",
"score": 5,
"reason": "季度激励",
"createTime": "2026-06-17T16:00:00+08:00"
}
}贡献申请接口
贡献申请接口用于三方系统提交贡献申请、查询行为标准、获取申请详情等。所有接口都必须通过 X-Employee-Virtual-Id 指定当前操作人。
apiKeyId 和 apiKeyName 字段,用于标识该申请来源于哪个三方应用。
获取贡献申请初始化参数,包括是否允许匿名申请、自定义申请的分值上下限等。
{
"statusCode": 100,
"msg": "获取成功",
"data": {
"allowAnonymous": true,
"minScore": 1,
"maxScore": 100
}
}获取行为标准列表(分页),支持按关键字搜索。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
page | int | 否 | 页码,默认 1。 |
pageSize | int | 否 | 每页条数,默认 20。 |
keyword | string | 否 | 行为标准名称关键字。 |
{
"statusCode": 100,
"msg": "获取成功",
"data": {
"total": 10,
"rows": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "优秀代码贡献",
"description": "对项目有显著贡献的代码提交",
"score": 10,
"category": "技术贡献"
}
]
}
}提交贡献申请,支持自定义申请和行为标准申请两种类型。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
applyType | int | 是 | 申请类型,0=行为标准申请,1=自定义申请。 |
targetEmployeeVirtualIds | string[] | 是 | 目标员工 virtualId 列表。 |
score | decimal | 否 | 申请分值(自定义申请时必填)。 |
reason | string | 否 | 申请理由。 |
attachs | string[] | 否 | 附件 URL 列表。 |
isAnonymous | bool | 否 | 是否匿名申请。 |
standardList | object[] | 否 | 行为标准申请项列表(行为标准申请时必填)。 |
standardList 项结构
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
standardItemId | string | 是 | 行为标准项 ID (UUID)。 |
score | decimal | 是 | 申请分值。 |
reason | string | 否 | 申请理由。 |
attachs | string[] | 否 | 附件 URL 列表。 |
{
"statusCode": 100,
"msg": "申请已提交",
"data": {
"applyId": "b34b6f3f-f84e-45ce-8c44-bb85d55a9ef5",
"message": "申请已提交"
}
}获取贡献申请详情。
{
"statusCode": 100,
"msg": "获取成功",
"data": {
"applyId": "b34b6f3f-f84e-45ce-8c44-bb85d55a9ef5",
"status": 1,
"applyType": 1,
"apiKeyId": "c56d7f8g-h90i-1234-jklm-no5678901234",
"apiKeyName": "我的三方应用",
"score": 10,
"reason": "优秀代码贡献",
"attachs": [],
"isAnonymous": false,
"createTime": "2026-06-18T10:00:00+08:00"
}
}附件接口
附件上传接口必须通过 X-Employee-Virtual-Id 指定当前操作人,上传后可把返回 URL 回填到任务创建请求的 attachmentUrls。
上传附件,表单字段名固定为 file。
X-Employee-Virtual-Id。未传时返回 403。{
"statusCode": 100,
"msg": "上传成功",
"data": {
"url": "https://cdn.example.com/task/20260617/demo.pdf",
"fileName": "demo.pdf",
"fileSize": 204800,
"id": "b34b6f3f-f84e-45ce-8c44-bb85d55a9ef5"
}
}错误码与排查
| HTTP 状态 | statusCode | 说明 |
|---|---|---|
| 400 | 400 / 101 | 请求参数错误,例如标题为空、附件为空、virtualId 无效等。 |
| 401 | 40101 | 缺少认证请求头。 |
| 401 | 40102 | API Key 无效。 |
| 401 | 40103 | 时间戳格式错误。 |
| 401 | 40104 | 请求已过期。 |
| 401 | 40105 | 签名校验失败。 |
| 401 | 40106 | Nonce 重复,或请求被重复使用。 |
| 403 | 40301 | API Key 已停用。 |
| 403 | 40302 | API Key 已过期。 |
| 403 | 40303 | IP 不在白名单中。 |
| 403 | 40304 | 操作人不属于当前企业,或当前员工上下文无效。 |
| 403 | 403 | 权限不足,或缺少 X-Employee-Virtual-Id,或当前 API Key 未授权指定能力。 |
| 404 | 404 | 员工、任务、绑定关系等资源不存在。 |
| 502 | 502 及其他大于 100 的状态码 | 开放平台网关调用下游内部服务失败,或下游返回结构异常。 |
| 500 | 500 | 系统异常。 |
HTTP 状态码 用于表示错误大类(如 401 认证失败、403 授权失败),statusCode 用于表示更细粒度、可供程序稳定判断的机器错误码;两者不要求一一对应。
[query]?... 形式写入 RequestBody,并按字段名统一脱敏。
常见排查项
- 确认 API Key 已启用、未过期,且命中白名单。
- 确认签名原文中的路径、QueryString、Body 与真实请求完全一致。
- 确认每次请求都重新生成
X-Timestamp、X-Nonce、X-Signature,不要在重试或并发请求中复用旧请求头。 - 确认需要当前员工身份的接口都传了
X-Employee-Virtual-Id。 - 确认被引用的员工
virtualId来自当前 API Key 下的/employees返回结果。