外观
对外集成
对外集成模块面向集团既有平台与第三方系统对接。它提供两块能力:一是 OAuth2 开放接口,第三方系统用 client_credentials 换取访问令牌后,按项目编码拉取项目快照;二是出站 Webhook,把智建云侧发生的关键业务事件(里程碑变更、重大隐患、质量 NCR、采购延误)实时推送到第三方回调地址。
本章读者
本章面向对接方的开发人员,包含接口地址、请求示例与验签代码。管理端的「新建客户端 / 新建 Webhook」等配置操作由项目侧具备集成权限的管理员完成,也可参照本章的操作步骤部分。
功能概述
- OAuth2 开放接口(
client_credentials模式):管理端创建开放客户端,签发client_id与client_secret;第三方凭此换取访问令牌,令牌有效期 2 小时。 - 项目快照拉取:持令牌调用快照接口,一次取回某项目的状态、健康分、计划/实际形象进度、里程碑与未闭环 NCR 等汇总指标。
- 出站 Webhook 订阅:管理端配置回调地址与订阅事件,业务事件发生时由平台主动 POST 推送。
- 推送带 HMAC-SHA256 签名(请求头
X-UanBuild-Signature),接收方用配置的密钥验签,确认来源与完整性。 - 投递失败自动重试(最多 5 次),管理端可查看投递记录、随时发送测试事件联调。
谁能进入本模块
左侧导航「系统集成 → 对外集成」对拥有 integration.view 权限的成员可见。新建、编辑客户端与 Webhook、发送测试事件需要 integration.manage 权限;仅有 integration.view 的成员只能查看列表与投递记录。
界面构成
进入「对外集成」后,页面顶部为两个标签页。
- Webhook 回调:Webhook 订阅列表(名称、回调地址、订阅事件、状态),右上角「新建 Webhook」按钮,每行提供「测试」与「投递记录」操作。
- 开放接口客户端:OAuth2 客户端列表(名称、
client_id、状态、创建时间),右上角「新建客户端」按钮,页脚附换取令牌的接口提示。
一、OAuth2 授权与项目快照
面向需要主动从智建云拉取数据的第三方系统。
步骤 1:创建开放客户端
- 进入「开放接口客户端」标签页,点击「新建客户端」。
- 填写「名称」(例如:集团数据平台),点击「创建」。
- 系统弹出
client_id与client_secret。请立即复制保存。
client_secret 只显示这一次
client_secret 仅在创建时明文返回一次,服务端只保存其哈希值,之后无法再次查看。若遗失只能作废该客户端重新创建。请妥善保管,勿写入前端代码或提交到代码仓库。
步骤 2:换取访问令牌
用 client_id 与 client_secret 调用令牌接口。租户码通过请求头 X-Tenant-Code 指定,用于定位你所属的租户。
- 接口:
POST /open/v1/oauth/token - 请求头:
X-Tenant-Code: <租户码>、Content-Type: application/json - 请求体:
grant_type(可选,若填写必须为client_credentials)、client_id、client_secret
bash
curl -X POST https://<智建云域名>/open/v1/oauth/token \
-H "X-Tenant-Code: <租户码>" \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "ub_xxxxxxxxxxxxxxxxxxxx",
"client_secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}'成功响应(平台统一包装为 { code, message, data },code=0 为成功):
json
{
"code": 0,
"message": "ok",
"data": {
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 7200
}
}令牌响应字段:
| 字段 | 说明 |
|---|---|
| access_token | 访问令牌,用于后续接口的 Authorization: Bearer 头 |
| token_type | 固定为 Bearer |
| expires_in | 有效期(秒),固定 7200,即 2 小时 |
令牌已自带租户信息
X-Tenant-Code 只在换取令牌时需要。令牌本身已绑定该租户,后续调用快照等接口时只需带 Authorization: Bearer,无需再传租户码。令牌过期后重新调用本接口换取即可,建议接入方缓存令牌并在临近过期前刷新。
步骤 3:拉取项目快照
持令牌按项目编码拉取该项目的汇总快照。
- 接口:
GET /open/v1/project/{项目编码}/snapshot - 请求头:
Authorization: Bearer <access_token>
bash
curl https://<智建云域名>/open/v1/project/<项目编码>/snapshot \
-H "Authorization: Bearer <access_token>"成功响应 data 示例:
json
{
"project_code": "PRJ-2026-001",
"project_name": "某炼化一体化项目",
"status": 1,
"health_score": 82,
"plan_progress": 63.5,
"actual_progress": 58.2,
"milestones_total": 24,
"milestones_at_risk": 3,
"open_ncr": 5,
"generated_at": "2026-07-11T09:30:00+08:00"
}项目快照字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| project_code | 字符串 | 项目编码 |
| project_name | 字符串 | 项目名称 |
| status | 整数 | 项目状态码 |
| health_score | 整数/空 | 项目健康分(0–100),未评估时为空 |
| plan_progress | 小数/空 | 计划形象进度(百分比),按叶子 WBS 等权均值计算,无数据时为空 |
| actual_progress | 小数/空 | 实际形象进度(百分比),口径同上 |
| milestones_total | 整数 | 里程碑总数 |
| milestones_at_risk | 整数 | 处于预警或滞后状态(状态码 4、5)的里程碑数 |
| open_ncr | 整数 | 未闭环的 NCR 数量 |
| generated_at | 字符串 | 快照生成时间(ISO 8601,东八区,格式 +08:00) |
二、出站 Webhook
面向需要被动接收智建云事件推送的第三方系统。
步骤 1:创建 Webhook 订阅
- 进入「Webhook 回调」标签页,点击「新建 Webhook」。
- 填写以下字段:
- 名称:便于识别的订阅名称。
- 回调地址:接收推送的 URL,必须以
http://或https://开头(生产环境建议 HTTPS)。 - 签名密钥:由你自定义的一段字符串,平台用它对推送体做 HMAC-SHA256 签名,你用同一密钥验签。请妥善保管,勿泄露。
- 订阅事件:可选「全部事件」或某一具体事件类型。
- 点击「创建」。此后订阅的事件发生时,平台会向该地址 POST 推送。
回调地址应尽快返回 2xx
接收端收到推送后应尽快返回 HTTP 2xx 状态码表示接收成功。单次投递的超时为 10 秒,超时或返回非 2xx 都会被判为失败并进入重试。请勿在响应链路里做耗时的同步处理,建议先落库/入队再异步处理。
事件目录
| 事件类型 | 触发场景 | 界面订阅项 |
|---|---|---|
| milestone.changed | 里程碑状态发生变更 | 里程碑变更 |
| hazard.major | 产生重大隐患 | — |
| ncr.created | 生成质量不符合项(NCR) | NCR 产生 |
| procurement.delay | 关键路径物资延误拖累里程碑 | 关键路径物资延误 |
订阅「全部事件」时接收上述所有事件;订阅具体某一类时只接收该类。接收端应始终以推送体内的 event 字段判断事件类型,并对未识别的类型做兼容处理。
事件推送格式
每次推送为一个 POST 请求,请求头包含:
| 请求头 | 说明 |
|---|---|
| Content-Type | application/json |
| X-UanBuild-Event | 事件类型(与推送体内 event 字段一致) |
| X-UanBuild-Signature | 签名,格式 sha256=<十六进制小写>,见「验证签名」 |
| User-Agent | uanbuild-backend/<版本号> |
推送体是一个 JSON 对象,始终包含标识事件类型的 event 字段,外加该事件的业务字段(含 project_id)。以下为两个代表性事件的字段说明。
ncr.created(质量 NCR 生成)示例与字段:
json
{
"event": "ncr.created",
"project_id": 12,
"ncr_id": 305,
"code": "NCR-2026-0012",
"title": "焊缝外观不合格",
"level": 2
}| 字段 | 说明 |
|---|---|
| event | 固定 ncr.created |
| project_id | 所属项目 ID |
| ncr_id | NCR 记录 ID |
| code | NCR 编号 |
| title | 问题标题 |
| level | 问题级别(数值,越大越严重) |
procurement.delay(关键路径物资延误)示例与字段:
json
{
"event": "procurement.delay",
"project_id": 12,
"milestone_id": 88,
"status": 5,
"status_text": "滞后",
"caused_by_order": 47
}| 字段 | 说明 |
|---|---|
| event | 固定 procurement.delay |
| project_id | 所属项目 ID |
| milestone_id | 受影响的里程碑 ID |
| status | 里程碑新状态码(4=预警,5=滞后) |
| status_text | 状态文字(预警 / 滞后) |
| caused_by_order | 造成延误的采购单 ID |
字段以推送体为准
不同事件的业务字段不同。接入方应按 event 字段分支解析,并对字段做防御式读取(缺省字段容错),以适应后续事件字段的扩展。milestone.changed、hazard.major 的推送体同样遵循上述封装:始终含 event 与 project_id,并携带对应业务对象的标识与关键信息。
验证签名
平台用该 Webhook 配置的「签名密钥」对推送的原始请求体计算 HMAC-SHA256,结果转为十六进制小写,放入请求头 X-UanBuild-Signature,格式为 sha256=<hex>。
接收端应对收到的原始请求体(未经反序列化改动的字节)用同一密钥重算,并与请求头比对。验签通过才处理事件。
验签伪代码:
text
signature_header = 请求头 X-UanBuild-Signature # 形如 "sha256=abcd..."
expected = "sha256=" + hex_lower(HMAC_SHA256(密钥, 原始请求体))
if constant_time_equal(signature_header, expected):
接受并处理事件
else:
拒绝(返回 4xx),记录可疑推送Python 示例:
python
import hashlib, hmac
def verify(secret: str, raw_body: bytes, signature_header: str) -> bool:
digest = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
expected = "sha256=" + digest
return hmac.compare_digest(expected, signature_header)Node.js 示例:
js
const crypto = require('crypto')
function verify(secret, rawBody, signatureHeader) {
const digest = crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
const expected = 'sha256=' + digest
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader))
}用原始请求体验签,并用恒定时间比较
必须对未经解析、未重新序列化的原始请求体字节计算签名。若先把 JSON 反序列化再序列化回字符串,字段顺序或空白差异会导致签名不匹配。比较签名时使用恒定时间比较函数(如上例的 compare_digest / timingSafeEqual),避免时序侧信道。
投递、重试与失败处理
- 每条事件对每个订阅它的启用中 Webhook 生成一条投递记录并入队推送。
- 返回 HTTP 2xx 视为投递成功;非 2xx 响应、连接失败或 10 秒超时均记为一次失败。
- 失败自动重试,单条投递累计尝试上限为 5 次;达到上限仍未成功则标记为「失败」,不再重试。
- 若某 Webhook 在重试期间被禁用或删除,其待投递记录会直接标记失败,不再推送。
接收端应做幂等处理
由于存在重试,同一事件可能被投递多次。请以事件内的业务标识(如 ncr_id、milestone_id)做去重,保证重复接收同一事件不会造成重复业务处理。
查看投递记录与发送测试事件
- 在「Webhook 回调」列表中,点击目标 Webhook 行的「投递记录」,查看最近 50 条投递(事件类型、状态、尝试次数、响应码、时间)。
- 联调时,点击「测试」向该 Webhook 发送一条测试事件,接收端可据此验证连通性与验签逻辑。测试事件推送体示例:
json
{
"event": "webhook.test",
"message": "这是一条测试事件",
"at": "2026-07-11T09:30:00+08:00"
}测试事件同样带签名
测试事件与正式事件走同一条投递通道,请求头 X-UanBuild-Event 为 webhook.test,并带 X-UanBuild-Signature 签名,可用来完整验证接收端的验签实现。仅启用中的 Webhook 可发送测试。
字段与状态说明
开放客户端列表字段:
| 字段 | 说明 |
|---|---|
| 名称 | 客户端名称 |
| client_id | 客户端标识(ub_ 前缀) |
| 状态 | 启用 / 停用(停用后无法换取令牌) |
| 创建时间 | 客户端创建时间 |
Webhook 列表字段:
| 字段 | 说明 |
|---|---|
| 名称 | 订阅名称 |
| 回调地址 | 接收推送的 URL |
| 订阅事件 | all(全部)或具体事件类型 |
| 状态 | 启用 / 停用(停用后不再推送,也不能发测试) |
投递记录状态:
| 状态 | 含义 |
|---|---|
| 待发 | 已入队,等待投递或等待下一次重试 |
| 成功 | 接收端返回 2xx,投递完成 |
| 失败 | 达到重试上限仍未成功,或 Webhook 已被禁用/删除 |
投递记录还包含尝试次数(attempts)、响应码(response_code,接收端返回的 HTTP 状态码)与最近一次错误信息(last_error),用于排查失败原因。
注意事项与常见问题
- 换令牌报「缺少 X-Tenant-Code 头」:令牌接口必须带
X-Tenant-Code请求头指定租户码,缺失会返回 400。 - 换令牌报「client 凭据无效」或「client 已禁用」:核对
client_id/client_secret是否正确、有无多余空格,以及该客户端是否已被停用。为防枚举,凭据错误与客户端不存在返回一致的提示。 - 调用快照报令牌无效或过期:令牌有效期 2 小时,过期需重新换取。请确认
Authorization头格式为Bearer <token>。 - 快照报「项目不存在」:确认路径中的项目编码属于当前令牌所绑定的租户,且项目未被删除。
- 验签总是不通过:确认使用的是「未经二次序列化的原始请求体字节」计算签名,且比对的是去掉
sha256=前缀后的十六进制小写值;密钥须与创建 Webhook 时填写的完全一致。 - 收到重复推送:属预期行为(重试机制所致),接收端需按业务标识做幂等去重。
- 测试事件发不出去:仅启用状态的 Webhook 可发送测试事件;若已停用,请先启用。