flash-express / php-push-sdk
FlashExpress Push Message SDK - 支持多场景 push 消息发送、站内信、设备 token 获取,通过 JSON-RPC 调用 ard-api / by_rpc
Requires
- php: >=7.2
- guzzlehttp/guzzle: ^6.5 || ^7.0
Requires (Dev)
- phpunit/phpunit: ^8.0 || ^9.0 || ^10.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
FlashExpress 推送消息 PHP SDK,支持单条/批量推送、站内信发送、设备 Token 获取,通过 JSON-RPC 调用 ard-api / by_rpc 接口。
环境要求
- PHP >= 7.2
- ext-curl
- ext-json
- ext-openssl
安装
composer require flash-express/php-push-sdk:^1.0
快速开始
初始化
<?php
use FlashExpress\Push\Client;
$client = new Client([
// 必填: ard-api SVC JSON-RPC 入口地址 (例如 /svc/call)
'ard_api_endpoint' => 'http://ard-api.svc/svc/call',
// 可选: by_rpc 地址 (用于获取待审批数量等)
'by_rpc_endpoint' => 'http://by-rpc.svc',
// 必填: push.* 接口签名鉴权凭据 (ard-api SignatureAuthMiddleware 强制校验)
'app_id' => 'your_app_id',
'app_key' => 'your_app_key',
// 可选: 语言环境, 默认 zh-CN
'locale' => 'zh-CN',
// 可选: HTTP 超时 (秒), 默认 10
'timeout' => 10,
// 可选: RPC 失败重试次数, 默认 0 (不重试)
'retry_times' => 0,
// 可选: 重试间隔 (毫秒), 默认 200
'retry_delay' => 200,
]);
API 接口
1. 单条推送 (按工号/客户 ID)
向单个指定用户发送 push 消息,SDK 自动获取设备 Token。
$response = $client->pushToStaff([
'src' => Client::SRC_BACKYARD, // 来源系统: c | ka | kit | backyard | osm
'staff_info_id' => '100001', // 工号或客户 ID
'message_title' => '您有新的审批待处理',
'message_content' => '请及时前往审批中心处理',
'message_scheme' => 'app://approval/detail', // 可选: 点击跳转 Scheme
'message_priority' => 0, // 可选: 0 普通 | 1 优先
'silence' => 0, // 可选: 0 有声音 | 1 静默推送
'thumbnail' => 'https://xxx.com/icon.png', // 可选: 图片推送 (iOS 富推送 / Android 图文)
]);
if ($response->isSuccess()) {
echo $response->getMessage();
} else {
echo $response->getMessage();
}
来源系统常量:
| 常量 | 值 | 说明 |
|---|---|---|
Client::SRC_C | 'c' | C端用户 |
Client::SRC_KA | 'ka' | KA客户 |
Client::SRC_KIT | 'kit' | KIT (快递员手持客户端) |
Client::SRC_BACKYARD | 'backyard' | OA (员工端) |
Client::SRC_OSM | 'osm' | OSM (外协员工管理客户端) |
Client::SRC_PAY | 'flashpay' | FlashPay (支付客户端) |
2. 批量推送 (按工号/客户 ID 列表)
一次性向最多 100 个用户推送消息,支持批量设置公共参数。
$response = $client->pushToStaffs(
// 每个用户独立参数
[
['staff_info_id' => '100001', 'src' => Client::SRC_BACKYARD],
['staff_info_id' => '100002', 'src' => Client::SRC_BACKYARD],
],
// 公共参数 (会被独立参数覆盖)
[
'message_title' => '团队通知',
'message_content' => '今日例会时间调整为 15:00',
'message_scheme' => 'app://meeting',
]
);
if ($response->isSuccess()) {
$data = $response->getData();
echo "总计: {$data['total']}, 成功: {$data['success']}, 失败: {$data['fail']}";
}
3. 直接设备 Token 推送
跳过设备 Token 查询,直接推送。适用于调用方已有设备 Token 的场景。
$response = $client->pushToDevices([
[
'os' => 'android', // 'android' | 'ios'
'device_token' => 'APA91b...', // 设备 Token
'device_type' => Client::DEVICE_TYPE_GOOGLE, // 可选: 1 Google | 2 华为
'staff_info_id' => '100001', // 可选: 关联工号
'src' => Client::SRC_OSM, // 可选, 默认 osm
'message_title' => '新订单来了',
'message_content' => '点击查看详情',
'message_scheme' => 'app://order',
'message_priority' => 0,
'badge' => 0,
],
]);
设备类型常量:
| 常量 | 值 |
|---|---|
Client::DEVICE_TYPE_GOOGLE | 1 |
Client::DEVICE_TYPE_HUAWEI | 2 |
4. 发送站内信
$response = $client->sendInternalMessage([
'staff_info_id' => '100001', // 工号, 多值传数组: ['100001', '100002']
'message_title' => '系统公告',
'message_content' => '春节期间服务调整通知...',
'category' => -1, // 可选: 分类 ID
'category_code' => 0, // 可选: 分类编码
'top_state' => 0, // 可选: 0 不置顶 | 1 置顶
'related_id' => 'order_20260901_001', // 可选: 关联业务 ID
]);
5. 组合方法: 站内信 + Push
一次调用完成站内信和 Push 发送,通过开关控制是否发送。
$response = $client->pushMsgByStaffs([
'staff_ids' => ['100001', '100002', '100003'],
// 通知内容
'src' => Client::SRC_BACKYARD,
'title' => '审批通知',
'content' => '您有一条新的审批待处理',
'push_content' => '审批待处理', // 可选: Push 与站内信内容不同时使用, 默认同 content
// 发送开关
'is_mail' => true, // 是否发送站内信, 默认 true
'is_push' => true, // 是否发送 push, 默认 true
// 后院 Badge 控制 (可选)
'is_by_badge' => true, // true = 使用指定 badge 值; false = 自动查询待审批数
'by_banding_counts' => [ // 当 is_by_badge=true 时, 每个员工的 badge 值
'100001' => 3,
'100002' => 1,
],
// 站内信扩展字段 (可选)
'category' => -1,
'category_code' => 0,
'top_state' => 0,
'related_id' => '',
]);
if ($response->isSuccess()) {
$data = $response->getData();
echo "站内信: 成功 {$data['internal_mail']['success']} / 失败 {$data['internal_mail']['fail']}\n";
echo "Push: 成功 {$data['push']['success']} / 失败 {$data['push']['fail']}\n";
}
响应格式
所有方法统一返回 FlashExpress\Push\Contracts\ResponseInterface 对象:
$response->isSuccess(); // code === 1
$response->isFail(); // code === 0
$response->isError(); // code === -1
$response->getCode(); // 1=成功 | 0=业务失败 | -1=系统/参数错误
$response->getMessage(); // 消息文本
$response->getData(); // 业务数据 (mixed)
$response->getRaw(); // 原始 JSON-RPC 响应 (string)
$response->toArray(); // 转数组 ['code'=>1, 'msg'=>'ok', 'data'=>...]
异常处理
SDK 不会主动抛出异常,所有错误都通过 Response::error() / Response::fail() 返回。
如需捕获底层异常,可 catch 以下类型:
| 异常类 | 触发场景 |
|---|---|
FlashExpress\Push\Exceptions\ValidationException | 配置项缺失 / 参数校验失败 |
FlashExpress\Push\Exceptions\RpcException | JSON-RPC 调用异常 |
FlashExpress\Push\Exceptions\PushException | SDK 通用异常 (基类) |
use FlashExpress\Push\Exceptions\RpcException;
use FlashExpress\Push\Exceptions\ValidationException;
try {
$response = $client->pushToStaff([...]);
} catch (ValidationException $e) {
echo '参数错误: ' . $e->getMessage();
echo '详细: ' . json_encode($e->getErrors());
} catch (RpcException $e) {
echo 'RPC 调用失败: ' . $e->getMessage();
}
鉴权配置
ard-api 的 push.* 开头的 JSON-RPC 接口已强制开启签名鉴权,由 SignatureAuthMiddleware 在 SVC Server 层统一拦截校验。SDK 自动完成签名计算并附加到每个请求,调用方只需传入凭据。
范围说明:仅对 JSON-RPC 方法名以
push.开头的接口生效(如push.sendToMQ、push.getDeviceToken、push.sendInternalMessage),其他 SVC 方法(如 by_rpc 的get_panding_count)不受影响,无需签名。
申请凭据
在 ard-api 侧为调用方分配一对 app_id / app_key,服务端通过以下方式之一验证:
- 推荐(生产):环境变量
PUSH_SDK_APPKEY_{APP_ID}(app_id 转大写) - 开发调试:
SignatureAuthMiddleware::APP_SECRETS常量数组
完整调用示例
use FlashExpress\Push\Client;
$client = new Client([
'ard_api_endpoint' => 'http://ard-api.svc/svc/call',
'app_id' => 'your_app_id', // 必填
'app_key' => 'your_app_key', // 必填
'locale' => 'zh-CN',
'timeout' => 10,
]);
$response = $client->pushToStaff([
'src' => Client::SRC_BACKYARD,
'staff_info_id' => '100001',
'message_title' => '您有新的审批待处理',
'message_content' => '请及时前往审批中心处理',
]);
if ($response->isSuccess()) {
echo "推送成功\n";
} else {
echo "推送失败: " . $response->getMessage() . "\n";
}
SDK 自动附加的 HTTP Header
所有对 push.* 方法的调用会自动附带以下 Header,调用方无需手动设置:
| Header | 说明 |
|---|---|
X-App-Id | 应用 ID |
X-Timestamp | 秒级时间戳,服务端 ±5 分钟 有效期 |
X-Nonce | 32 位随机十六进制字符串 |
X-Signature | 签名值 |
签名算法(与服务端 SignatureAuthMiddleware 一致)
stringToSign = X-Timestamp + X-Nonce + json_encode(params, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES)
X-Signature = hash_hmac('sha256', stringToSign, app_key)
注意:签名仅对 JSON-RPC 请求体的
params字段计算,不包含jsonrpc/id/method等其他顶层字段。
常见鉴权失败
| 服务端返回 | 原因 | 排查方向 |
|---|---|---|
缺少必要 Header | SDK 未传 app_id / app_key 或值为空 | 确认初始化配置已传凭据 |
无效的 App ID | app_id 在服务端未注册 | 检查 PUSH_SDK_APPKEY_{APP_ID} 环境变量或常量 |
时间戳已过期 | 服务器时间与客户端差超过 5 分钟 | 检查服务器/客户端 NTP 同步 |
签名不匹配 | 签名计算逻辑不一致 | 确认 SDK 版本与服务端 SignatureAuthMiddleware 一致 |
调试技巧
开启 debug 模式可在日志中看到每次请求的完整 Header、Payload 和响应:
$client->setDebug(true);
// 或直接在 config 里传 'debug' => true
$response = $client->pushToStaff([...]);
// 最近一次请求的完整调试上下文
$debug = $client->getDebugInfo();
echo json_encode($debug, JSON_PRETTY_PRINT);
// 包含: endpoint / method / payload / headers / http_code / curl_error / response / duration_ms
架构说明
┌──────────────┐ JSON-RPC ┌──────────────┐
│ Push SDK │ ───────────▶ │ ard-api │
│ (Client) │ sendToMQ │ PushController
└──────┬───────┘ getDeviceToken └──────────────┘
│
│ JSON-RPC
▼
┌──────────────┐
│ by_rpc │
│ (待审批数) │
└──────────────┘
SDK 通过 push.* 方法名调用 ard-api SVC 接口,通过 get_panding_count 调用 by_rpc。
License
MIT