Skip to content

08 — 待办与通知公告推送(第三方集成手册)

读者:第三方业务系统开发 / 联调人员
前提:已按 01-接入准备.md 拿到 client_idclient_secret,并绑定 system_code
字段级权威:事项 02、公告 07;运行时见 Knife4j /doc.html
设计背景(非必读):doc/36doc/16

本文把两条入站管道写在一起,便于选型与一次性联调。


1. 先选型:走哪条管道?

你的业务调用用户在门户哪里看
审批待办、要办结/重开/改状态事项 /api/open/matters个人「事项 / 待办」
流程抄送、少数人知会(跟某单绑定)事项matterType=NOTICE 等 + parties同上
放假/制度等广播公告(全员、按部门、用户列表)公告 /api/open/notices个人「通知公告」收件箱
需要管理端「已发公告」与撤回停投递公告同上
text
第三方系统

    ├─ HMAC ──► /api/open/matters/**     → portal_matter*     → /api/portal/matters

    └─ HMAC ──► /api/open/notices/**     → portal_notice*     → /api/notice/inbox

禁止:同一业务主键既推事项又推公告(用户会看到两条)。


2. 公共约定(两条管道相同)

2.1 鉴权头

Header必填说明
X-Client-Id门户下发的 client_id
X-TimestampUnix (与门户时钟偏差 ≤ 300 秒)
X-SignatureHMAC-SHA256 的 Hex(大小写不敏感)
X-Idempotency-Key事项记 portal_matter_push_log;公告记 portal_notice_push_log(管理端消息日志可查,非对接必调)

2.2 签名

text
签名串 = clientId + "\n" + timestamp + "\n" + rawBody
签名   = Hex( HMAC-SHA256( client_secret, 签名串 ) )
注意说明
rawBody必须与 HTTP 实际发送的 JSON 字节完全一致(勿先对象再二次 JSON.stringify 改空格/字段序)
system_code只认 Client 绑定,body 里带了也会被忽略
TLS生产须 HTTPS;一期应用层加密信封(L3)

Node.js

js
const crypto = require('crypto');
function sign(clientId, secret, timestamp, rawBody) {
  const payload = `${clientId}\n${timestamp}\n${rawBody}`;
  return crypto.createHmac('sha256', secret).update(payload, 'utf8').digest('hex');
}

Java(Hutool)

java
String payload = clientId + "\n" + timestamp + "\n" + rawBody;
String signature = new HMac(HmacAlgorithm.HmacSHA256, secret.getBytes(UTF_8)).digestHex(payload);

2.3 统一响应

json
{ "code": 0, "message": "success", "data": { }, "traceId": "..." }
  • code === 0 为成功(鉴权失败也常是 HTTP 200 + code=401/403)。
  • 事项 / 公告:落库成功才 code=0,失败应重试。

2.4 幂等键

管道幂等
事项system_code + externalId(UPSERT 可更新)
公告system_code + externalId仅首次 publish;再次 → 409

externalId 由你们生成并保持稳定(建议业务单号 / 公告号)。


3. 事项管道速查(/api/open/matters

完整字段见 02-事项推送-HMAC.md

方法路径用途
POST/upsert创建或更新 + 参与人
POST/status改办理态 / 第三方原文态
POST/reopen重开
POST/delete软删(仍是 POST,不是 HTTP DELETE)

3.1 待办 UPSERT 最小示例

json
{
  "matterType": "TODO",
  "externalId": "WF-1001",
  "title": "请假审批",
  "bizStatus": "PENDING",
  "pcUrl": "https://oa.example.com/wf/1001",
  "parties": [
    { "username": "zhangsan", "partyRole": "ASSIGNEE" }
  ]
}

partyRoleASSIGNEE 承办 / CC 抄送 / RECIPIENT 知会。username 须为门户已存在登录名。

3.2 流程知会(仍走事项,不是公告)

json
{
  "matterType": "NOTICE",
  "externalId": "WF-1001-CC",
  "title": "抄送:请假审批",
  "parties": [
    { "username": "lisi", "partyRole": "RECIPIENT" }
  ]
}

3.3 curl(事项)

bash
BASE="https://portal.example.com"
CLIENT_ID="demo-client"
SECRET="your-client-secret"
TS=$(date +%s)
BODY='{"matterType":"TODO","externalId":"WF-1001","title":"请假审批","bizStatus":"PENDING","parties":[{"username":"zhangsan","partyRole":"ASSIGNEE"}]}'
SIG=$(node -e "const c=require('crypto');const b=process.argv[1],id=process.argv[2],ts=process.argv[3],s=process.argv[4];console.log(c.createHmac('sha256',s).update(id+'\n'+ts+'\n'+b).digest('hex'))" "$BODY" "$CLIENT_ID" "$TS" "$SECRET")

curl -sS -X POST "$BASE/api/open/matters/upsert" \
  -H "Content-Type: application/json" \
  -H "X-Client-Id: $CLIENT_ID" \
  -H "X-Timestamp: $TS" \
  -H "X-Signature: $SIG" \
  -d "$BODY"

4. 公告管道速查(/api/open/notices

完整字段见 07-通知公告推送-HMAC.md

方法路径用途
POST/publish首次发布;同键再发 409
POST/recall撤回(inbox 不再展示)

4.1 指定用户

json
{
  "externalId": "ANN-20260728-001",
  "noticeType": "NOTICE",
  "title": "系统维护通知",
  "summary": "今晚 22:00–23:00",
  "content": "详细说明…",
  "pcUrl": "https://app.example.com/ann/1",
  "audience": {
    "mode": "USERS",
    "usernames": ["zhangsan", "lisi"]
  }
}

4.2 按部门(含下级)

json
{
  "externalId": "ANN-DEPT-001",
  "title": "部门制度更新",
  "audience": {
    "mode": "DEPTS",
    "deptCodes": ["D001"],
    "includeSubDepts": true
  }
}

4.3 全员

json
{
  "externalId": "ANN-ALL-001",
  "title": "国庆放假安排",
  "audience": { "mode": "ALL" }
}

预估收件人 > 500 时门户异步投递:data.dispatchMode=ASYNC,可带 taskCode;用户仍从通知公告收件箱读取。

4.4 撤回

json
{ "externalId": "ANN-20260728-001" }

4.5 curl(公告)

bash
BASE="https://portal.example.com"
CLIENT_ID="demo-client"
SECRET="your-client-secret"
TS=$(date +%s)
BODY='{"externalId":"ANN-1001","title":"放假通知","audience":{"mode":"USERS","usernames":["zhangsan"]}}'
SIG=$(node -e "const c=require('crypto');const b=process.argv[1],id=process.argv[2],ts=process.argv[3],s=process.argv[4];console.log(c.createHmac('sha256',s).update(id+'\n'+ts+'\n'+b).digest('hex'))" "$BODY" "$CLIENT_ID" "$TS" "$SECRET")

curl -sS -X POST "$BASE/api/open/notices/publish" \
  -H "Content-Type: application/json" \
  -H "X-Client-Id: $CLIENT_ID" \
  -H "X-Timestamp: $TS" \
  -H "X-Signature: $SIG" \
  -d "$BODY"

成功时 data 示例:

json
{
  "dispatchMode": "SYNC",
  "noticeId": 1234567890123456789,
  "noticeCode": "ANN-...",
  "externalId": "ANN-1001",
  "dispatchStatus": "SUCCESS",
  "estimatedRecipients": 1
}

5. 常见错误

code含义处理
401缺头 / 签名错 / 时间窗外 / 未知 Client对时钟、核对 rawBody、secret
403Client 停用 / 无 secret / 未绑 system_code找门户管理员
400参数非法、受众为空等按字段表修正
404事项/公告不存在核对 externalId
409公告已发布过同键;或事项侧冲突公告勿重复 publish;事项用 upsert/status

6. 联调检查清单

  1. [ ] CONFIDENTIAL Client + client_secret + 已绑 system_code + 启用
  2. [ ] 本机与门户时间偏差 < 5 分钟
  3. [ ] HMAC 用原始 body 字符串签名,Content-Type=application/json
  4. [ ] 事项:门户用户能在「事项」看到 TODO;username 真实存在
  5. [ ] 公告:门户用户能在「通知公告」看到;看不到事项列表里
  6. [ ] 公告二次相同 externalId409recall 后收件箱消失
  7. [ ] 不以 HTTP 状态码单独判成功,以 code===0 为准

7. 文档索引

文档用途
01-接入准备.mdClient / 响应 / 公共错误
02-事项推送-HMAC.md事项四接口字段级
07-通知公告推送-HMAC.md公告 publish/recall 字段级
本文 08选型 + 双管道速查 + 示例
README.md全部第三方能力总览
sdk/README.mdSDK / Demo 下载索引

8. 下载 Demo