本文档独立成篇,描述如何在业务系统中对接 Unified Authentication Platform(UAP,统一认证平台)。阅读者应能仅凭本文完成:配置项填写、登录、SSO、用户查询与映射同步、消息发送(系统通知与广播)。
下文示例中:
https://api.hnyunzhu.com/api/v1 表示 UAP_API_BASE(生产环境统一认证 API);app_demo_001、your_app_secret、eyJhbG... 等为占位符,需替换为真实值;sign 为按第 4 节算法计算得到的 64 位小写十六进制字符串,示例里用 deadbeef... 形式表示,不能直接复制使用;1710000000 仅为示例,调用时需使用当前 Unix 秒。| 变量名 | 含义 | 示例 |
|---|---|---|
UAP_API_BASE |
HTTP(S) API 根路径,必须包含版本前缀 /api/v1 |
https://api.hnyunzhu.com/api/v1 |
UAP_WEB_BASE |
用户浏览器访问的前端站点根(用于跳转登录页;本部署与 API 同主机) | https://api.hnyunzhu.com |
下文所有接口路径均相对于 UAP_API_BASE 拼接,例如:UAP_API_BASE + /simple/login → https://api.hnyunzhu.com/api/v1/simple/login。
跳转登录页使用 UAP_WEB_BASE:
{UAP_WEB_BASE}/login?app_id=<APP_ID>{UAP_WEB_BASE}/mobile/login?app_id=<APP_ID>在 UAP 管理后台创建应用后,请收集并安全保存以下信息:
| 配置项 | 说明 |
|---|---|
APP_ID |
应用对外标识(字符串,如 app_xxxxxxxx) |
APP_SECRET |
用于 HMAC-SHA256 签名,仅允许在业务服务端使用,禁止写入前端或客户端 |
APP_ACCESS_TOKEN |
应用访问令牌,用于 M2M 接口(HTTP Header:X-App-Access-Token) |
REDIRECT_URIS |
在应用中配置的回调地址列表;Redirect SSO、sso-login、exchange 等流程依赖合法回调 |
可选:若使用标准 OIDC 协议,还需 OIDC 客户端凭据(本文不展开 OIDC 全流程)。
实施对接时,请将贵司从 UAP 管理后台获取的值填入下表(可打印或复制后替换正文中的占位符)。App Secret 仅保存在服务端,勿提交到代码仓库或前端。 下表中 API / 前端根地址 已按当前生产环境填写;若贵司使用独立域名部署,请改为实际地址。
| 项目 | 变量名(本文档) | 填写 |
|---|---|---|
| API 根地址 | UAP_API_BASE |
https://api.hnyunzhu.com/api/v1 |
| 前端站点根地址 | UAP_WEB_BASE |
https://api.hnyunzhu.com |
| App ID | APP_ID |
|
| App Secret | APP_SECRET |
|
| Access Token(应用访问令牌) | APP_ACCESS_TOKEN |
说明:
X-App-Id / X-Sign 等。X-App-Access-Token,对应 M2M(用户搜索、映射同步等);不是 App Secret。UAP_API_BASE 必须包含版本前缀 /api/v1;UAP_WEB_BASE 为浏览器访问登录页所用站点根(本部署与 API 同主机时为 https://api.hnyunzhu.com)。对接时会遇到四类身份校验,请勿混用:
| 方式 | 典型场景 | 请求形式 |
|---|---|---|
| A. JSON Body 签名(Simple Auth) | /simple/login、/simple/validate、/simple/exchange 等 |
Body 内含 timestamp、sign,用 APP_SECRET 对除 sign 外的参数做 HMAC-SHA256(见第 4 节) |
| B. HTTP Header 签名(应用调消息接口) | 服务端代应用调用 POST /messages/ |
Header:X-App-Id、X-Timestamp、X-Sign(签名字符串仅含 app_id 与 timestamp,见第 4.2 节) |
| C. 用户 JWT | 用户已登录 UAP 后调用需身份的接口(如 POST /auth/login/json、管理类接口等) |
Authorization: Bearer <access_token> |
| D. M2M 访问令牌 | 用户搜索、映射同步、全量用户拉取 | X-App-Access-Token: <APP_ACCESS_TOKEN>(部分场景亦支持应用 JWT Bearer,与平台实现一致) |
平台校验逻辑与实现一致:参数键名按 ASCII 升序排列,拼接为 key1=value1&key2=value2(不含 sign),再对拼接串做 HMAC-SHA256,密钥为 UTF-8 编码的 APP_SECRET,结果为小写 十六进制字符串。
时间戳:timestamp 为 Unix 秒级整数;服务端对时间偏差有约 300 秒容差。
参与签名的键为请求 JSON 中除 sign 以外的所有键(None 值一般应排除,与常见实现一致);仅包含实际发送的字段。各接口以下文「参与签名的字段」为准。
Python 示例:
import hmac
import hashlib
def sign_simple_auth(secret: str, params: dict) -> str:
data = {k: v for k, v in params.items() if k != "sign" and v is not None}
query_string = "&".join(f"{k}={data[k]}" for k in sorted(data.keys()))
return hmac.new(
secret.encode("utf-8"),
query_string.encode("utf-8"),
hashlib.sha256,
).hexdigest()
仅对两个键签名:app_id(即 X-App-Id 的值)、timestamp(即 X-Timestamp 的字符串值,与 Header 中一致)。拼接串为:
app_id=<APP_ID>×tamp=<TIMESTAMP>
对该字符串做 HMAC-SHA256,结果放入 X-Sign。
消息 Header 签名示例(伪代码):
app_id=app_demo_001×tamp=1710000000X-Timestamp 取值与字符串中的 timestamp 完全一致(通常为数字的十进制字符串,如 "1710000000")。X-App-Id: app_demo_001
X-Timestamp: 1710000000
X-Sign: 1a2b3c4d5e6f708192a3b4c5d6e7f8090a1b2c3d4e5f678901234567890abcd
POST /simple/login(密码登录)application/jsonapp_id 时为平台账号登录(返回用户 JWT);提供 app_id 时为应用 SSO 登录(返回 ticket)。应用侧若提供 sign / timestamp,二者须同时出现,且服务端会校验签名(推荐服务端调用时始终带签名)。| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
app_id |
string | null | 否 | 不提供:平台登录;提供:应用 SSO,返回 ticket |
identifier |
string | 是 | 用户标识:手机号、映射 mapped_key 或映射邮箱等(平台登录时按手机号查找) |
password |
string | 是 | 登录密码 |
remember_me |
boolean | 否 | 默认 false;为 true 时 JWT 有效期延长(仅平台登录生效) |
timestamp |
int | 条件 | 与 sign 同时提供或同时省略;提供时参与签名 |
sign |
string | 条件 | HMAC-SHA256 十六进制;应用 SSO 时推荐必带 |
应用 SSO 且启用签名时,参与签名的字段为:app_id、identifier、password、timestamp(不含 remember_me)。
200:PasswordLoginResponse| 字段 | 类型 | 何时出现 | 说明 |
|---|---|---|---|
ticket |
string | null | 提供 app_id 且登录成功 |
临时票据,供 POST /simple/validate 使用 |
access_token |
string | null | 未提供 app_id 且登录成功 |
用户 JWT |
token_type |
string | null | 平台登录成功 | 固定为 bearer |
role |
string | null | 平台登录成功 | 如 SUPER_ADMIN、DEVELOPER、ORDINARY_USER |
应用 SSO 成功时典型返回:{ "ticket": "<票据字符串>" }(无 access_token)。
A. 应用 SSO(带签名,返回 ticket)
POST https://api.hnyunzhu.com/api/v1/simple/login HTTP/1.1
Content-Type: application/json
{
"app_id": "app_demo_001",
"identifier": "13800138000",
"password": "UserPassword123",
"timestamp": 1710000000,
"sign": "1a2b3c4d5e6f708192a3b4c5d6e7f8090a1b2c3d4e5f678901234567890abcd"
}
sign 由 APP_SECRET 对以下键签名(不含 sign):app_id、identifier、password、timestamp。
响应 200:
{
"ticket": "TICKET-7f8e9d0a-1234-5678-abcd-ef0123456789"
}
B. 平台登录(无 app_id,返回 JWT)
POST https://api.hnyunzhu.com/api/v1/simple/login HTTP/1.1
Content-Type: application/json
{
"identifier": "13800138000",
"password": "UserPassword123",
"remember_me": false
}
响应 200:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0In0.xxx",
"token_type": "bearer",
"role": "ORDINARY_USER"
}
错误示例(密码错误,401):
{
"detail": "密码错误"
}
detail(节选)| 状态码 | 含义(示例) |
|---|---|
| 400 | 用户已禁用、签名无效(应用侧) |
| 401 | 密码错误 |
| 404 | 用户未找到、应用未找到 |
POST /simple/sms-login(短信验证码登录)application/jsonapp_id 返回平台 JWT;有 app_id 返回 ticket。签名可选;若带 sign 必须同时带 timestamp。短信功能需在平台开启(否则可能 403)。| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
mobile |
string | 是 | 手机号 |
code |
string | 是 | 短信验证码 |
remember_me |
boolean | 否 | 默认 false;影响平台登录 JWT 有效期 |
app_id |
string | null | 否 | 不提供:平台登录;提供:应用 SSO,返回 ticket |
timestamp |
int | 条件 | 与 sign 成对出现 |
sign |
string | 条件 | 应用 SSO 且启用签名时:参与签名的字段为 app_id、mobile、code、timestamp |
200与 5.1 相同,使用 PasswordLoginResponse(ticket 或 access_token + token_type + role)。
应用 SSO(带签名)
POST https://api.hnyunzhu.com/api/v1/simple/sms-login HTTP/1.1
Content-Type: application/json
{
"app_id": "app_demo_001",
"mobile": "13800138000",
"code": "123456",
"timestamp": 1710000000,
"sign": "2b3c4d5e6f708192a3b4c5d6e7f8090a1b2c3d4e5f678901234567890abcde"
}
参与签名的键:app_id、mobile、code、timestamp。
响应 200:
{
"ticket": "TICKET-8a9b0c1d-2345-6789-bcde-f01234567890"
}
平台短信登录(无 app_id)
{
"mobile": "13800138000",
"code": "123456",
"remember_me": false
}
响应 200:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"role": "ORDINARY_USER"
}
| 状态码 | 含义(示例) |
|---|---|
| 400 | 验证码错误或已过期、用户已禁用 |
| 403 | 短信登录功能未开启 |
| 404 | 用户未找到、应用未找到 |
POST /simple/validate(验证票据)application/jsonAPP_SECRET 对 Body 签名(sign / timestamp 必填)。| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
ticket |
string | 是 | 来自登录跳转、exchange、sso-login 等流程的票据 |
app_id |
string | 是 | 消费票据的应用 ID(与为该应用签名的 Secret 一致) |
timestamp |
int | 是 | Unix 秒 |
sign |
string | 是 | 参与签名的字段为:ticket、app_id、timestamp |
200:TicketValidateResponse票据有效时:
| 字段 | 类型 | 说明 |
|---|---|---|
valid |
bool | true |
user_id |
int | 平台用户主键 |
mobile |
string | 用户手机号 |
mapped_key |
string | null | 该用户在当前应用下的映射账号(无映射时为 null) |
mapped_email |
string | null | 该用户在当前应用下的映射邮箱 |
票据无效或已消费:
| 字段 | 类型 | 说明 |
|---|---|---|
valid |
bool | false |
user_id、mobile 等 |
— | 一般为 null 或省略(以实际 JSON 为准) |
参与签名的键:ticket、app_id、timestamp(不含 sign)。
POST https://api.hnyunzhu.com/api/v1/simple/validate HTTP/1.1
Content-Type: application/json
{
"app_id": "app_demo_001",
"ticket": "TICKET-7f8e9d0a-1234-5678-abcd-ef0123456789",
"timestamp": 1710000001,
"sign": "3c4d5e6f708192a3b4c5d6e7f8090a1b2c3d4e5f678901234567890abcdef"
}
响应 200(成功):
{
"valid": true,
"user_id": 1001,
"mobile": "13800138000",
"mapped_key": "ext_user_001",
"mapped_email": "zhangsan@example.com"
}
响应 200(票据无效或已使用):
{
"valid": false
}
错误示例(签名错误,400):
{
"detail": "签名无效"
}
| 状态码 | 含义(示例) |
|---|---|
| 400 | 签名无效 |
| 404 | 应用未找到 |
POST /auth/login/jsonapplication/json请求体:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
mobile |
string | 是 | 用户手机号(平台账号) |
password |
string | 是 | 密码 |
remember_me |
boolean | 否 | 默认 false;true 时使用更长有效期 JWT |
app_id |
string | null | 否 | 可选;直接平台登录通常可省略 |
响应 200:
| 字段 | 类型 | 说明 |
|---|---|---|
access_token |
string | 用户 JWT |
token_type |
string | 固定为 bearer |
常见错误: 400(手机号或密码错误、账户已禁用)、403(账户待审核)。
POST /auth/login/json
POST https://api.hnyunzhu.com/api/v1/auth/login/json HTTP/1.1
Content-Type: application/json
{
"mobile": "13800138000",
"password": "UserPassword123",
"remember_me": true
}
响应 200:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer"
}
POST /auth/login(表单)
POST https://api.hnyunzhu.com/api/v1/auth/login HTTP/1.1
Content-Type: application/x-www-form-urlencoded
username=13800138000&password=UserPassword123
响应 200: 与 JSON 登录相同。
错误示例(400):
{
"detail": "手机号或密码错误"
}
(POST /auth/login 表单字段与响应与上表一致,见上文 HTTP 示例。)
非 JSON 接口,步骤如下:
{UAP_WEB_BASE}/login?app_id=<APP_ID> 或 {UAP_WEB_BASE}/mobile/login?app_id=<APP_ID>。ticket=<票据>。POST /simple/validate(见 5.3)换用户信息并建立会话。前提:回调地址必须在应用 REDIRECT_URIS 中配置。
浏览器打开登录页(仅说明 URL,无 JSON Body):
https://api.hnyunzhu.com/login?app_id=app_demo_001
登录成功后,浏览器跳转到应用回调(示例):
https://biz.example.com/oauth/callback?ticket=TICKET-7f8e9d0a-1234-5678-abcd-ef0123456789
业务后端再使用 5.3 的 POST /simple/validate 换用户信息。
POST /simple/sso-login(API 获取跳转 URL)application/jsonticket 的完整 redirect_url,客户端应导航至该 URL。目标应用须存在;SIMPLE_API 应用返回 回调地址?ticket=...;OIDC 应用返回简化后的根 URL(仅 scheme+netloc),详见下表。| 模式 | 请求头 / Body |
|---|---|
| 会话模式 | Authorization: Bearer <用户 access_token>;Body 仅需目标应用 app_id |
| 凭据模式 | 无 Bearer;Body 须含 app_id、username、password |
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
app_id |
string | 是 | 要进入的目标应用 ID |
username |
string | 条件 | 未带有效 Bearer 时必填:手机号、映射 key 或映射邮箱等 |
password |
string | 条件 | 未带有效 Bearer 时必填 |
200:SsoLoginResponse| 字段 | 类型 | 说明 |
|---|---|---|
redirect_url |
string | SIMPLE_API:{首个 redirect_uri}?ticket=<票据>;OIDC:回调 URI 解析后的 scheme://host[:port](无路径) |
| 状态码 | 含义(示例) |
|---|---|
| 400 | 应用未找到、未配置重定向 URI、用户已禁用、协议不支持等 |
| 401 | 认证失败 |
会话模式(已持有用户 JWT)
POST https://api.hnyunzhu.com/api/v1/simple/sso-login HTTP/1.1
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
{
"app_id": "target_app_002"
}
响应 200(SIMPLE_API 应用):
{
"redirect_url": "https://biz.example.com/callback?ticket=TICKET-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
}
凭据模式(未登录)
POST https://api.hnyunzhu.com/api/v1/simple/sso-login HTTP/1.1
Content-Type: application/json
{
"app_id": "target_app_002",
"username": "13800138000",
"password": "UserPassword123"
}
响应 200: 同上,为带 ticket 的 redirect_url。
POST /simple/exchange(源应用用户免登进目标应用)application/jsonAPP_SECRET;sign / timestamp 必填。| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
app_id |
string | 是 | 源应用 ID |
target_app_id |
string | 是 | 目标应用 ID |
user_mobile |
string | 是 | 用户在 UAP 的手机号(须已存在) |
timestamp |
int | 是 | Unix 秒 |
sign |
string | 是 | 参与签名:app_id、target_app_id、user_mobile、timestamp |
200:TicketExchangeResponse| 字段 | 类型 | 说明 |
|---|---|---|
ticket |
string | 目标应用可用的票据 |
redirect_url |
string | 目标应用首个 redirect_uri 拼接 ?ticket=<ticket> 的完整 URL(若未配置合法 URI,实现中可能回退占位,生产环境应保证配置正确) |
| 状态码 | 含义(示例) |
|---|---|
| 400 | 签名无效 |
| 404 | 源应用 / 目标应用 / 用户未找到 |
目标应用收到 ticket 后,用目标应用的 app_id + APP_SECRET 调用 POST /simple/validate。
参与签名的键:app_id、target_app_id、user_mobile、timestamp(使用源应用 Secret)。
POST https://api.hnyunzhu.com/api/v1/simple/exchange HTTP/1.1
Content-Type: application/json
{
"app_id": "source_app_001",
"target_app_id": "target_app_002",
"user_mobile": "13800138000",
"timestamp": 1710000002,
"sign": "4d5e6f708192a3b4c5d6e7f8090a1b2c3d4e5f678901234567890abcdef01"
}
响应 200:
{
"ticket": "TICKET-bbbbcccc-dddd-eeee-ffff-000011112222",
"redirect_url": "https://target.example.com/callback?ticket=TICKET-bbbbcccc-dddd-eeee-ffff-000011112222"
}
GET /simple/sso/jump(通知内嵌 SSO 跳转)用于消息中配置的跳转:用户点击后,若已在 UAP 登录则带上 Ticket 重定向到应用回调。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
app_id |
string | 是 | 目标应用 ID |
redirect_to |
string | 是 | 登录成功后最终要到达的业务页 URL(经 URL 编码传入) |
| 场景 | HTTP | 说明 |
|---|---|---|
| 当前未登录 UAP | 307/302 等 |
RedirectResponse 到相对路径 /login?redirect=<当前完整 jump URL>(依赖网关/前端将用户导向登录页) |
| 当前已登录 | 307/302 等 |
重定向到应用 redirect_uris 中首个 URI,查询参数包含 ticket、next(next 为 redirect_to) |
| 应用不存在 | 404 | detail 说明 |
| 未配置回调 | 400 | detail 说明 |
请求 URL(redirect_to 需 URL 编码):
GET https://api.hnyunzhu.com/api/v1/simple/sso/jump?app_id=app_demo_001&redirect_to=https%3A%2F%2Foa.example.com%2Fapprove%2F123
已登录时: HTTP 重定向(302/307),Location 类似:
https://biz.example.com/callback?ticket=TICKET-xxx&next=https%3A%2F%2Foa.example.com%2Fapprove%2F123
未登录时: 重定向到登录页,Location 可能类似:
/login?redirect=https%3A%2F%2Fapi.hnyunzhu.com%2Fapi%2Fv1%2Fsimple%2Fsso%2Fjump%3Fapp_id%3D...
以下接口使用 X-App-Access-Token: <APP_ACCESS_TOKEN>(或实现所支持的应用 JWT),不使用 Simple Auth 的 Body 签名。
对接方在「把业务账号与平台用户绑定」时,建议按下面顺序操作:
先查询用户
调用 GET /users/search(见 7.2),用手机号、姓名等关键词确认平台是否已存在该用户,并记录返回中的 id(平台用户 ID)、mobile、name 等。
mapped_key、mapped_email 等)。POST /users/ 创建(需管理员权限);或POST /apps/mapping/sync 的 UPSERT:当手机号在平台不存在时,可在同一请求中携带 name 等,由平台新建用户并建立映射(见 7.3)。填充本应用账号信息并同步
使用查询到的 mobile(及必要时 name)与业务侧 mapped_key / mapped_email,调用 POST /apps/mapping/sync:
sync_action 为 UPSERT(默认),平台会插入或更新当前应用下的映射;若本次同时新建了平台用户,响应中 new_user_created 为 true。UPSERT,可更新 mapped_key、mapped_email、is_active 等;不可通过该接口修改已存在用户的姓名、手机号等基础档案(与接口校验一致)。sync_action 为 DELETE(仅删映射,不删平台用户)。全量对账(可选)
需要批量拉取平台用户基础信息时,使用 GET /apps/mapping/users(见 7.4)。
GET /users/search(用户查询)GET /users/searchX-App-Access-Token: <APP_ACCESS_TOKEN>,或 Authorization: Bearer <用户 JWT>| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
q |
string | 否 | — | 关键词;对手机号、姓名、英文名模糊匹配(ilike)。不传时返回一批活跃用户(受 limit 限制) |
limit |
int | 否 | 20 | 最大返回条数 |
说明:若使用用户 JWT 调用,结果中会排除当前登录用户本人;使用 应用令牌 时无此排除。
200返回 JSON 数组,元素为平台用户对象,主要字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
id |
int | 平台用户 ID(可用于发消息时的 receiver_id 等) |
mobile |
string | 手机号 |
name |
string | null | 姓名 |
english_name |
string | null | 英文名 |
status |
string | 如 ACTIVE |
role |
string | null | 角色 |
created_at |
string | 创建时间 ISO8601 |
updated_at |
string | 更新时间 ISO8601 |
is_deleted |
int | 是否删除标记 |
GET https://api.hnyunzhu.com/api/v1/users/search?q=13800138000&limit=20 HTTP/1.1
X-App-Access-Token: pA9s8d7f6g5h4j3k2l1m0n9o8p7q6r5s4t3u2v1w0
响应 200 示例:
[
{
"id": 1001,
"mobile": "13800138000",
"name": "张三",
"english_name": "zhangsan",
"status": "ACTIVE",
"role": "ORDINARY_USER",
"created_at": "2025-01-10T08:00:00",
"updated_at": "2025-06-01T12:00:00",
"is_deleted": 0
}
]
POST /apps/mapping/syncapplication/jsonUserSyncRequest| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
mobile |
string | 是 | 中国大陆手机号,正则 ^1[3-9]\d{9}$(与平台校验一致) |
name |
string | null | 条件 | UPSERT 时必填(非空姓名);已存在用户时不可通过此接口改姓名等基础信息;DELETE 可不依赖姓名逻辑(以 sync_action 分支为准) |
english_name |
string | null | 否 | 已废弃于 M2M;英文名由平台按规则生成 |
password |
string | null | 否 | 按平台实现(若有) |
status |
string | null | 否 | 按平台实现 |
mapped_key |
string | 是 | 外部系统用户 ID,长度 1~100 |
mapped_email |
string | null | 否 | 外部邮箱 |
is_active |
boolean | null | 否 | 映射是否启用;null 表示不修改 |
sync_action |
string | null | 否 | UPSERT(默认)或 DELETE |
200:MappingResponse| 字段 | 类型 | 说明 |
|---|---|---|
id |
int | 映射记录 ID |
app_id |
int | 应用数据库主键(整数) |
user_id |
int | 平台用户 ID |
mapped_key |
string | null | 映射账号 |
mapped_email |
string | null | 映射邮箱 |
user_mobile |
string | 用户手机号 |
user_status |
string | 用户统一状态描述 |
is_active |
bool | 映射是否有效;DELETE 后可能为 false |
new_user_created |
bool | 是否本次新建了平台用户 |
generated_password |
string | null | 若平台为新用户生成了初始密码,可能返回 |
| 状态码 | 含义(节选) |
|---|---|
| 400 | 参数非法、与业务规则冲突(如重复映射、禁止修改字段) |
| 403 | Token 无效 |
UPSERT
POST https://api.hnyunzhu.com/api/v1/apps/mapping/sync HTTP/1.1
Content-Type: application/json
X-App-Access-Token: pA9s8d7f6g5h4j3k2l1m0n9o8p7q6r5s4t3u2v1w0
{
"mobile": "13800138000",
"name": "张三",
"mapped_key": "ext_user_1001",
"mapped_email": "zhangsan@partner.com",
"is_active": true,
"sync_action": "UPSERT"
}
响应 200:
{
"id": 501,
"app_id": 12,
"user_id": 1001,
"mapped_key": "ext_user_1001",
"mapped_email": "zhangsan@partner.com",
"user_mobile": "13800138000",
"user_status": "ACTIVE",
"is_active": true,
"new_user_created": true,
"generated_password": null
}
DELETE(仅删除映射)
POST https://api.hnyunzhu.com/api/v1/apps/mapping/sync HTTP/1.1
Content-Type: application/json
X-App-Access-Token: pA9s8d7f6g5h4j3k2l1m0n9o8p7q6r5s4t3u2v1w0
{
"mobile": "13800138000",
"mapped_key": "ext_user_1001",
"sync_action": "DELETE"
}
响应 200: 结构同 MappingResponse,is_active 可能为 false(见字段表)。
GET /apps/mapping/usersX-App-Access-Token| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
skip |
int | 否 | 0 | 跳过条数 |
limit |
int | 否 | 100 | 每页条数 |
200:UserSyncList| 字段 | 类型 | 说明 |
|---|---|---|
total |
int | 符合条件的用户总数 |
items |
array | 用户列表 |
items[] 元素 UserSyncSimple:
| 字段 | 类型 | 说明 |
|---|---|---|
mobile |
string | 手机号 |
name |
string | null | 姓名 |
english_name |
string | null | 英文名 |
GET https://api.hnyunzhu.com/api/v1/apps/mapping/users?skip=0&limit=100 HTTP/1.1
X-App-Access-Token: pA9s8d7f6g5h4j3k2l1m0n9o8p7q6r5s4t3u2v1w0
响应 200:
{
"total": 1250,
"items": [
{
"mobile": "13800138000",
"name": "张三",
"english_name": "zhangsan"
},
{
"mobile": "13900139000",
"name": "李四",
"english_name": "lisi"
}
]
}
POST /messages/)本节仅说明由应用服务端调用、使用 Header 签名(第 3 节方式 B、第 4.2 节)发送 NOTIFICATION 的两种形态:
不涉及用户间私信(type: MESSAGE)及用户 JWT 代发,对接方无需关注。
POST /messages/application/json| Header | 说明 |
|---|---|
Content-Type |
application/json |
X-App-Id |
应用字符串 ID,与 APP_ID 一致 |
X-Timestamp |
Unix 秒(与签名、Body 中逻辑一致) |
X-Sign |
对 app_id=<X-App-Id>×tamp=<X-Timestamp> 的 HMAC-SHA256 十六进制 |
content_type 取值说明与后端枚举一致,发送时传字符串(大写)。未传时默认 TEXT。
| 取值 | 含义 | content 建议形态 |
|---|---|---|
TEXT |
普通文本通知 | 字符串 |
IMAGE |
图片 | 对象存储中的 Object Key 或经平台处理的资源标识;若误传完整 URL,服务端可能尝试抽取 Key |
VIDEO |
视频 | 同上 |
FILE |
文件 | 同上 |
USER_NOTIFICATION |
业务/申请类通知(会话列表中突出标题、正文可结构化) | 字符串或 JSON 对象;传对象时服务端会序列化为 JSON 字符串存储,客户端常对 content 做 JSON.parse 解析 |
与 auto_sso 的关系:当 auto_sso=true 且已提供 target_url、应用 app_id 时,只要 type 为 NOTIFICATION 或 content_type 为 USER_NOTIFICATION,平台都会按同一规则生成 SSO jump 形式的 action_url(见下文示例)。
选用建议:常规系统通知用 TEXT;需要「申请单样式」、富文本结构或前端自定义渲染时,可改用 USER_NOTIFICATION 并在 content 中传结构化数据。
type: NOTIFICATION,非广播)向一名接收者推送。接收人二选一指定:
receiver_id:平台用户 ID(整数);或app_id + app_user_id:应用字符串 ID + 该用户在本应用映射中的外部账号。| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type |
string | 是 | 固定 NOTIFICATION |
title |
string | 是 | 标题,最大 255 字 |
content |
string | object | 是 | 正文;content_type 为 USER_NOTIFICATION 时可传对象,见 8.1 content_type 取值说明 |
content_type |
string | 否 | 默认 TEXT;可选:TEXT、IMAGE、VIDEO、FILE、USER_NOTIFICATION(见 8.1) |
receiver_id |
int | 条件 | 与 app_id+app_user_id 二选一 |
app_id |
string | 条件 | 与 app_user_id 同时出现时用于解析接收人 |
app_user_id |
string | 条件 | 接收方在本应用映射中的 ID |
action_url |
string | null | 否 | 自定义跳转;若 auto_sso 为 true 可能被平台改写 |
action_text |
string | null | 否 | 按钮文案 |
target_url |
string | null | 否 | auto_sso=true 时作为 SSO 目标页,用于生成 jump 链接 |
auto_sso |
boolean | 否 | 默认 false;为 true 时可生成 /api/v1/simple/sso/jump?... 形式的 action_url |
sender_app_user_id |
string | null | 否 | 可选,标识应用侧「发起人」映射,用于审计展示 |
约束:is_broadcast 为 false 或不传;须提供 receiver_id 或 (app_id + app_user_id)。
请求 / 响应示例(按 app_user_id 指定接收人,含 SSO 跳转)
POST https://api.hnyunzhu.com/api/v1/messages/ HTTP/1.1
Content-Type: application/json
X-App-Id: app_demo_001
X-Timestamp: 1710000000
X-Sign: 1a2b3c4d5e6f708192a3b4c5d6e7f8090a1b2c3d4e5f678901234567890abcd
{
"app_id": "app_demo_001",
"app_user_id": "ext_user_1001",
"type": "NOTIFICATION",
"content_type": "TEXT",
"title": "审批提醒",
"content": "您有一条待办审批",
"auto_sso": true,
"target_url": "https://oa.example.com/approve/123",
"action_text": "去处理"
}
{
"id": 90001,
"sender_id": null,
"receiver_id": 1001,
"app_id": 12,
"app_name": "演示应用",
"type": "NOTIFICATION",
"content_type": "TEXT",
"title": "审批提醒",
"content": "您有一条待办审批",
"action_url": "/api/v1/simple/sso/jump?app_id=app_demo_001&redirect_to=https%3A%2F%2Foa.example.com%2Fapprove%2F123",
"action_text": "去处理",
"is_read": false,
"created_at": "2026-04-07T10:00:00",
"read_at": null
}
补充示例(用平台用户 ID 指定接收人:receiver_id)
POST https://api.hnyunzhu.com/api/v1/messages/ HTTP/1.1
Content-Type: application/json
X-App-Id: app_demo_001
X-Timestamp: 1710000001
X-Sign: <按 X-App-Id 与 X-Timestamp 计算的签名>
{
"type": "NOTIFICATION",
"content_type": "TEXT",
"title": "到账提醒",
"content": "您的订单已支付成功",
"receiver_id": 1001
}
响应体结构与上一示例相同,字段值随请求变化(如 receiver_id 为 1001)。
补充示例(content_type: USER_NOTIFICATION,结构化 content + SSO)
POST https://api.hnyunzhu.com/api/v1/messages/ HTTP/1.1
Content-Type: application/json
X-App-Id: app_demo_001
X-Timestamp: 1710000002
X-Sign: <按 X-App-Id 与 X-Timestamp 计算的签名>
{
"app_id": "app_demo_001",
"app_user_id": "ext_user_1001",
"type": "NOTIFICATION",
"content_type": "USER_NOTIFICATION",
"title": "请假申请待审批",
"content": {
"applicant": "张三",
"days": 3,
"reason": "年假"
},
"auto_sso": true,
"target_url": "https://oa.example.com/hr/leave/999",
"action_text": "去审批"
}
响应 200(节选): content 存为 JSON 字符串;content_type 为 USER_NOTIFICATION;action_url 在 auto_sso 为 true 时同样可生成 jump 链接。
{
"type": "NOTIFICATION",
"content_type": "USER_NOTIFICATION",
"title": "请假申请待审批",
"content": "{\"applicant\":\"张三\",\"days\":3,\"reason\":\"年假\"}",
"action_url": "/api/v1/simple/sso/jump?app_id=app_demo_001&redirect_to=..."
}
仅应用可调;向平台内全部活跃用户各投递一条通知。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
is_broadcast |
boolean | 是 | 必须为 true |
type |
string | 是 | 必须为 NOTIFICATION |
title |
string | 是 | 标题 |
content |
string | object | 是 | 正文 |
content_type |
string | 否 | 默认 TEXT;可选值见 8.1(USER_NOTIFICATION 等) |
auto_sso |
boolean | 否 | 是否对跳转链接做 SSO 封装 |
target_url |
string | null | 否 | 与 auto_sso 配合使用 |
action_url |
string | null | 否 | 自定义跳转(不走 SSO 时) |
action_text |
string | null | 否 | 按钮文案 |
约束:不要传 receiver_id、app_user_id;广播仅支持通知类型。
请求示例
POST https://api.hnyunzhu.com/api/v1/messages/ HTTP/1.1
Content-Type: application/json
X-App-Id: app_demo_001
X-Timestamp: 1710000003
X-Sign: 5e6f708192a3b4c5d6e7f8090a1b2c3d4e5f678901234567890abcdef0123
{
"is_broadcast": true,
"type": "NOTIFICATION",
"content_type": "TEXT",
"title": "系统维护",
"content": "今晚 22:00-24:00 维护"
}
响应 200: 返回 MessageResponse,与「为全体用户各创建一条通知」中的第一条记录对应(便于对接方拿到一条结构化回包);其余用户的消息结构相同,仅 id、receiver_id 等不同。若平台无活跃用户则返回 400,detail 为「没有可发送的活跃用户」。
{
"id": 90010,
"sender_id": null,
"receiver_id": 1001,
"app_id": 12,
"app_name": "演示应用",
"type": "NOTIFICATION",
"content_type": "TEXT",
"title": "系统维护",
"content": "今晚 22:00-24:00 维护",
"action_url": null,
"action_text": null,
"is_read": false,
"created_at": "2026-04-07T10:00:00",
"read_at": null
}
MessageResponse(发送接口共性)| 字段 | 类型 | 说明 |
|---|---|---|
id |
int | 消息 ID |
sender_id |
int | null | 发送方用户 ID;应用系统通知可能为 null |
receiver_id |
int | 接收方用户 ID |
app_id |
int | null | 应用数据库主键(整数) |
app_name |
string | null | 应用名称 |
type |
string | 本文档场景下为 NOTIFICATION(平台尚存在其他类型,见 OpenAPI) |
content_type |
string | 内容类型:TEXT、IMAGE、VIDEO、FILE、USER_NOTIFICATION |
title |
string | 标题 |
content |
string | 正文 |
action_url |
string | null | 跳转链接(系统通知可能存在) |
action_text |
string | null | 按钮文案 |
is_read |
bool | 是否已读 |
created_at |
datetime | 创建时间 ISO8601 |
read_at |
datetime | null | 已读时间 |
返回示例(200 OK,单条系统通知,与上文发送示例对应):
{
"id": 90001,
"sender_id": null,
"receiver_id": 1001,
"app_id": 12,
"app_name": "演示应用",
"type": "NOTIFICATION",
"content_type": "TEXT",
"title": "审批提醒",
"content": "您有一条待办审批",
"action_url": "/api/v1/simple/sso/jump?app_id=app_demo_001&redirect_to=https%3A%2F%2Foa.example.com%2Fapprove%2F123",
"action_text": "去处理",
"is_read": false,
"created_at": "2026-04-07T10:00:00",
"read_at": null
}
(广播成功时结构相同,通常 receiver_id 为本次作为「代表返回」的那条消息对应的用户 ID;action_url、app_id 等以实际响应为准。)
https://api.hnyunzhu.com/api/v1/docs(Swagger)。APP_SECRET 仅留在服务端;任何含 Secret 的签名不得在浏览器完成。UAP_API_BASE 与 UAP_WEB_BASE。APP_ACCESS_TOKEN 与 JWT 同样属于敏感凭据。文档版本:与当前仓库后端 Schema/路由一致时,API 前缀为 /api/v1。