Search by

flash-express / php-push-sdk

githubfisher

FlashExpress Push Message SDK - 支持多场景 push 消息发送、站内信、设备 token 获取,通过 JSON-RPC 调用 ard-api / by_rpc

Package info

gitee.com/hifisher/php-push-sdk.git

pkg:composer/flash-express/php-push-sdk

Statistics

Installs: 34

Dependents: 0

Suggesters: 0

v1.2.5 2026-10-14 09:47 UTC

This package is auto-updated.

Last update: 2026-10-14 09:47:51 UTC


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 个用户推送消息。外层 message_* 字段作为公共参数,list 内每个 item 可单独覆盖(内层优先,缺失字段自动回落到外层)。

SDK 内部自动检测"统一内容"模式:当 list 中每个 item 只含 staff_info_id 和 src 两个字段时,说明消息内容一致,会按 os + src + device_type 分组合并 device_token 为数组,一条队列消息最多携带 100 个 token,大幅减少 MQ 写入量。

情形一: 统一内容 (推荐, 消息内容一致时使用)

$response = $client->pushToStaffs([
    // 外层公共参数: 所有用户共享同一消息内容
    'message_title'    => '团队通知',
    'message_content'  => '今日例会时间调整为 15:00',
    'message_scheme'   => 'app://meeting',
    'message_priority' => 0,
    'src'              => Client::SRC_BACKYARD

    // 每个 item 只需 staff_info_id + src, 消息内容全部取自外层
    // SDK 会自动合并同 os+src+device_type 的设备 token, 一条消息最多 100 个
    'list' => [
        ['staff_info_id' => '100001'],
        ['staff_info_id' => '100002'],
        ['staff_info_id' => '100003'],
        // ... 最多 100 个
    ],
]);

if ($response->isSuccess()) {
    $data = $response->getData();
    echo "总计: {$data['total']}, 成功: {$data['success']}, 失败: {$data['fail']}";
}

情形二: 非统一内容 (每个用户消息不同)

$response = $client->pushToStaffs([
    // 外层公共参数 (被 list 内层 item 覆盖, 内层缺失的字段从此处取)
    'message_title'    => '团队通知',
    'message_content'  => '今日例会时间调整为 15:00',
    'message_scheme'   => 'app://meeting',
    'message_priority' => 0,

    // 每个 item 可携带额外字段覆盖外层, 消息内容各不相同
    'list' => [
        ['staff_info_id' => '100001', 'src' => Client::SRC_BACKYARD],
        ['staff_info_id' => '100002', 'src' => Client::SRC_BACKYARD],
        [
            'staff_info_id'   => '100003',
            'src'             => Client::SRC_KA,
            'message_title'   => '单独标题',     // 仅覆盖此条的 title, 其他继续取外层
            'message_content' => '单独内容',
        ],
    ],
]);

if ($response->isSuccess()) {
    $data = $response->getData();
    echo "总计: {$data['total']}, 成功: {$data['success']}, 失败: {$data['fail']}";
}

3. 直接设备 Token 推送

跳过设备 Token 查询,直接推送。适用于调用方已有设备 Token 的场景。

SDK 内部自动检测"统一内容"模式:当 list 中每个 item 只含 os、device_token、device_type、src(或其子集)时,说明消息内容一致,会按 os + src + device_type 分组合并 device_token 为数组,一条队列消息最多携带 100 个 token。

情形一: 统一内容 (推荐, 消息内容一致时使用)

$response = $client->pushToDevices([
    // 外层公共参数: 所有设备共享同一消息内容
    'message_title'    => '新订单来了',
    'message_content'  => '点击查看详情',
    'message_scheme'   => 'app://order',
    'message_priority' => 0,
    'src'              => Client::SRC_OSM

    // 每个 item 只需 os/device_token/device_type/src, 消息内容全部取自外层
    // SDK 会自动合并同 os+src+device_type 的设备 token, 一条消息最多 100 个
    'list' => [
        ['os' => 'android', 'device_token' => 'token1', 'device_type' => Client::DEVICE_TYPE_GOOGLE],
        ['os' => 'android', 'device_token' => 'token2', 'device_type' => Client::DEVICE_TYPE_HUAWEI],
        ['os' => 'ios',     'device_token' => 'token3', 'device_type' => Client::DEVICE_TYPE_APPLE],
        // ... 最多 100 个
    ],
]);

if ($response->isSuccess()) {
    $data = $response->getData();
    echo "总计: {$data['total']}, 成功: {$data['success']}, 失败: {$data['fail']}";
}

情形二: 非统一内容 (每个设备消息不同)

$response = $client->pushToDevices([
    // 外层公共参数 (被 list 内层 item 覆盖, 内层缺失的字段从此处取)
    'message_priority' => 0,

    // 每个 item 可携带额外字段覆盖外层, 消息内容各不相同
    'list' => [
        [
            'os'              => 'android',
            'device_token'    => 'APA91b...',
            'device_type'     => Client::DEVICE_TYPE_GOOGLE,
            'staff_info_id'   => '100001',
            'src'             => Client::SRC_OSM,
            'message_title'   => '新订单来了',
            'message_content' => '点击查看详情',
            'message_scheme'  => 'app://order',
            'badge'           => 0,
        ],
        [
            'os'              => 'ios',
            'device_token'    => 'abc123...',
            'device_type'     => Client::DEVICE_TYPE_APPLE,
            'staff_info_id'   => '100002',
            'src'             => Client::SRC_KIT,
            'message_title'   => '配送提醒',
            'message_content' => '您有新的配送任务',
            'message_scheme'  => 'app://delivery',
        ],
    ],
]);

if ($response->isSuccess()) {
    $data = $response->getData();
    echo "总计: {$data['total']}, 成功: {$data['success']}, 失败: {$data['fail']}";
}

设备类型常量:

常量值
Client::DEVICE_TYPE_GOOGLE1
Client::DEVICE_TYPE_HUAWEI2

响应格式

所有方法统一返回 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\RpcExceptionJSON-RPC 调用异常
FlashExpress\Push\Exceptions\PushExceptionSDK 通用异常 (基类)
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-Nonce32 位随机十六进制字符串
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 等其他顶层字段。

常见鉴权失败

服务端返回原因排查方向
缺少必要 HeaderSDK 未传 app_id / app_key 或值为空确认初始化配置已传凭据
无效的 App IDapp_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

请求超时设置

默认超时时间在初始化时通过 timeout 配置项设定(秒,默认 10)。如需在运行时临时调整,可调用 setTimeout(),对后续所有 RPC 调用立即生效。

// 初始化时设定默认超时
$client = new Client([
    'ard_api_endpoint' => 'http://ard-api.svc/svc/call',
    'app_id'           => 'your_app_id',
    'app_key'          => 'your_app_key',
    'timeout'          => 10,  // 默认 10 秒
]);

// 批量推送可能耗时较长, 临时调大超时
$client->setTimeout(30);
$response = $client->pushToStaffs([...]);

// 复位为默认值
$client->setTimeout(10);

// 支持链式调用
$response = $client->setTimeout(30)->pushToStaffs([...]);

架构说明

┌──────────────┐   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