# perk-pushplus-sdk **Repository Path**: pushplus/perk-pushplus-sdk ## Basic Information - **Project Name**: perk-pushplus-sdk - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2026-06-17 - **Last Updated**: 2026-06-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # perk-pushplus-sdk [PushPlus(推送加)](https://www.pushplus.plus) 官方接口的 Java SDK,覆盖**消息发送接口**与**全部开放接口**。 - 纯 Java SDK + Spring Boot Starter 双模块设计 - Java 17+、JDK 内置 `HttpClient`(无 OkHttp 等额外重依赖) - AccessKey **自动获取、缓存、过期前刷新、失效自动重试**,调用方无感知 - **本地限流守卫**:发送接口命中 `code=900`(请求次数过多)时自动短路同 token 的后续调用,避免无效请求与账号进一步受限([官方建议](https://www.pushplus.plus/doc/guide/code.html)) - 单条 `/send`、多渠道 `/batchSend`、消息回调(`message_complate` / `add_topic_user` / `add_friend`)类型化解析 - 全部开放接口:消息、用户、消息令牌、群组、群组用户、好友、Webhook、公众号/企业微信/邮箱渠道、ClawBot、功能设置、预处理、图片服务 - 完善的 Builder 模式与强类型枚举(`Channel`、`Template`、`SendStatus`、`WebhookType`、`CallbackEvent`、`ErrorCode`) ## 模块说明 | 模块 | 说明 | | --- | --- | | `perk-pushplus-sdk-core` | 核心 SDK,纯 Java,无任何框架依赖,可在任意 Java 项目中使用 | | `perk-pushplus-sdk-spring-boot-starter` | Spring Boot 自动装配,配置即用 | | `perk-pushplus-sdk-example` | 使用示例(不发布) | ## 快速开始 ### 1. 引入依赖 普通 Java 项目: ```xml com.perk-net perk-pushplus-sdk-core 1.1.0 ``` Spring Boot 项目: ```xml com.perk-net perk-pushplus-sdk-spring-boot-starter 1.1.0 ``` ### 2. 发送一条消息(最简) ```java PushPlusClient client = PushPlusClient.builder() .token("your_user_token") // 必填 .secretKey("your_secret_key") // 调用开放接口才需要 .build(); String shortCode = client.sendSimple("标题", "Hello PushPlus"); ``` ### 3. 使用 Builder 发送 Markdown / HTML / JSON 消息 ```java client.send(SendRequest.builder() .title("部署完成") .content("# v1.0.0\n- env: prod") .template(Template.MARKDOWN) .topic("ops") // 群组编码 .channel(Channel.WECHAT) // 默认就是 WECHAT,可省略 .callbackUrl("https://you/cb") // 异步回调 .timestamp(System.currentTimeMillis() + 5000) // 时效控制 .build()); ``` ### 4. 多渠道发送(`/batchSend`) ```java List result = client.batchSend(BatchSendRequest.builder() .title("多渠道告警") .content("CPU > 90%") .channel(Channel.WECHAT).option("") .channel(Channel.WEBHOOK).option("bark") .channel(Channel.EXTENSION).option("") .build()); ``` `channel(...)` 与 `option(...)` 可累计调用,SDK 自动用逗号拼接,与官方文档示例语义一致。 ### 5. 异步发送 ```java CompletableFuture future = client.sendAsync( SendRequest.builder().title("t").content("c").build()); ``` ## 开放接口(全量) 需要在 PushPlus 后台「开发设置」中:开启开放接口、配置 `secretKey`、把调用方所在公网 IP 加入安全 IP 列表。 ```java PushPlusClient client = PushPlusClient.builder() .token("user_token") .secretKey("xxx") .build(); // AccessKey 完全自动管理 —— 直接调用就好 UserInfo me = client.getUser().myInfo(); PageResult msgs = client.getOpenMessage().list(PageQuery.of(1, 20)); SendMessageResult r = client.getOpenMessage().queryResult(shortCode); // 群组 PageResult topics = client.getTopic().list(TopicListQuery.of(1, 20, 0)); TopicQrCode qr = client.getTopic().qrCode(/*topicId*/ 1, /*second*/ 86400, /*scanCount*/ -1); // Webhook 渠道配置 client.getWebhook().add(WebhookSaveRequest.builder() .webhookCode("bark") .webhookName("我的 Bark") .webhookType(WebhookType.BARK.getCode()) .webhookUrl("https://api.day.app/xxxx") .build()); // 功能设置 client.getSetting().changeIsSend(1); // 图片服务(一行上传到 PushPlus 图床) ImageUploadResult img = client.getImage().uploadFile(Path.of("logo.png")); String url = img.getUrl(); // 直接拿到可访问的图片地址 PageResult imgs = client.getImage().list(PageQuery.of(1, 10)); client.getImage().delete(imgs.getList().get(0).getId()); ``` 各 API 一览: | getter | 对应文档章节 | | --- | --- | | `client.getMessage()` | 二/三 发送消息接口 | | `client.getAccessKey()` | 一 获取 AccessKey(一般无需手动调用) | | `client.getOpenMessage()` | 二 消息接口(开放) | | `client.getUser()` | 三 用户接口 | | `client.getMessageToken()` | 四 消息 token 接口 | | `client.getTopic()` | 五 群组接口 | | `client.getTopicUser()` | 六 群组用户接口 | | `client.getWebhook()` | 七 渠道配置 - webhook | | `client.getChannel()` | 七 渠道配置 - 公众号/企业微信/邮箱 | | `client.getClawBot()` | 八 微信 ClawBot 接口 | | `client.getSetting()` | 九 功能设置接口 | | `client.getFriend()` | 十 好友功能接口 | | `client.getPre()` | 十一 预处理信息接口 | | `client.getImage()` | 十二 图片服务接口 | ## 图片服务 PushPlus 提供基于七牛云的图片图床(30 天有效,可主动删除)。SDK 把"获取上传凭证 → 多端表单上传 → 解析返回 URL"封装成一步: ```java // 1) 最常用:一行上传本地文件,得到可访问的图片 URL ImageUploadResult r = client.getImage().uploadFile(Path.of("/tmp/logo.png")); String url = r.getUrl(); // 2) 直接上传字节 client.getImage().uploadBytes(bytes, "screenshot.png"); // 3) 已上传图片列表 PageResult page = client.getImage().list(PageQuery.of(1, 10)); // 4) 主动删除(未删除的图片默认 30 天后由系统自动清理) client.getImage().delete(page.getList().get(0).getId()); ``` 如果你的业务需要自己控制凭证的获取与上传过程(例如缓存 token、分布式上传),也可以拆开调用: ```java ImageUploadToken token = client.getImage().getUploadToken(); ImageUploadResult r = client.getImage().upload(token, bytes, "a.png", "image/png"); ``` > 注意:上传图片的真正请求会按七牛云规范以 `multipart/form-data` 提交到 `uploadUrl`,**不会**携带 PushPlus 的 `access-key`;其余三个接口(获取凭证 / 列表 / 删除)走 PushPlus 开放接口,自动带上 `access-key`。 ## 消息回调解析 PushPlus 在消息发送完成、群组新增用户、新增好友时会回调你预置的 URL。SDK 提供类型安全的解析器: ```java @PostMapping("/pushplus/callback") public String callback(@RequestBody String body) { CallbackPayload p = CallbackParser.parse(body); switch (p.getEvent()) { case MESSAGE_COMPLETE -> handle(p.getMessageInfo()); // SendStatus 枚举可直接拿 case ADD_TOPIC_USER -> handle(p.getTopicUserInfo()); case ADD_FRIEND -> handle(p.getFriendInfo(), p.getQrCode()); } return "ok"; } ``` ## Spring Boot 集成 `application.yml`: ```yaml pushplus: token: ${PUSHPLUS_TOKEN} secret-key: ${PUSHPLUS_SECRET_KEY} # 调用开放接口必填 base-url: https://www.pushplus.plus connect-timeout: 10s read-timeout: 30s access-key-refresh-ahead-seconds: 300 log-request: false rate-limit-guard-enabled: true # 命中 code=900 后本地短路,默认开 rate-limit-cooldown: # 留空表示禁推到次日 0 点;可显式写 2d / 24h 等 ``` 随后即可注入: ```java @Service public class NotifyService { private final PushPlusClient pushPlus; public NotifyService(PushPlusClient pushPlus) { this.pushPlus = pushPlus; } public void onAlert(String msg) { pushPlus.sendSimple("告警", msg); } } ``` 如需自定义 HTTP 实现,往容器中放一个 `HttpRequester` Bean 即可被自动注入到 `PushPlusClient`。 ## 配置项 | 字段 | 默认 | 说明 | | --- | --- | --- | | `token` | – | 用户 token / 消息 token,发送消息使用 | | `secretKey` | – | 用户 secretKey,调用开放接口使用 | | `baseUrl` | `https://www.pushplus.plus` | 服务地址 | | `connectTimeout` | 10s | 连接超时 | | `readTimeout` | 30s | 读超时 | | `accessKeyRefreshAheadSeconds` | 300 | AccessKey 提前刷新秒数(PushPlus 文档说明老 key 在新 key 生成后 5 分钟内仍可用) | | `logRequest` | false | 开启 DEBUG 级别的请求/响应日志 | | `rateLimitGuardEnabled` | true | 是否启用本地限流守卫;命中 `code=900` 后短路同 token 的发送请求 | | `rateLimitCooldown` | – | 命中 `code=900` 后的本地禁推时长;为空表示到"次日 0 点",可显式设置 `Duration.ofDays(2)` | ## 异常处理 所有错误都会包装成 `PushPlusException`: - HTTP 调用失败:`code` 为 HTTP 状态码 - 业务接口返回 `code != 200`:`code` 为业务码、`message` 为业务消息 - SDK 参数校验失败:`code = -1` - **本地限流守卫命中**(不发起 HTTP):`code = 900`、`message` 包含 "本地限流守卫" 字样 `ErrorCode` 枚举已经把官方文档的全部业务码语义化(`OK / NOT_LOGIN / UNAUTHORIZED / IP_FORBIDDEN / SERVER_ERROR / DATA_ERROR / FORBIDDEN_VIEW / INSUFFICIENT_POINTS / RATE_LIMITED / INVALID_TOKEN / NOT_VERIFIED / VALIDATION_ERROR`),无需再用魔法数字判断。 ```java try { client.sendSimple("t", "c"); } catch (PushPlusException e) { if (e.isRateLimited()) { // 等价于 e.getErrorCode() == ErrorCode.RATE_LIMITED log.warn("PushPlus 限流,今天暂停推送: {}", e.getMessage()); return; } switch (e.getErrorCode()) { case INVALID_TOKEN -> log.error("token 错误,立即排查配置"); case NOT_VERIFIED -> log.error("账号未实名认证"); case INSUFFICIENT_POINTS -> log.warn("积分不足"); default -> log.warn("PushPlus 失败: code={}, msg={}", e.getCode(), e.getMessage()); } } ``` 参考:[PushPlus 接口返回码说明](https://www.pushplus.plus/doc/guide/code.html)。 ## 限流守卫(code=900 自动短路) PushPlus 在请求次数过多时会返回 `code=900`,官方文档明确建议"根据返回值判断当天是否让程序继续调用发送消息接口,否则会让账号进一步受限"。SDK 默认替你做这件事: - 任意一次 `client.send(...)` / `client.batchSend(...)` 命中 `code=900` 后,SDK 会按 token 维度记下"禁推至 X 时刻"。 - 同 token 后续发送调用不再发起 HTTP,直接抛 `PushPlusException(code=900, msg="本地限流守卫…")`。 - 默认禁推到**系统时区的次日 0 点**;通过 `rateLimitCooldown` 可改为固定时长(例如文档示例的 2 天)。 - 仅作用于 `MessageApi`(`send` / `batchSend`),开放接口不受影响。 - 进程内单例,**不跨进程共享**——多实例部署时每个进程最多被命中一次。 可观察 / 可干预: ```java RateLimitGuard guard = client.getRateLimitGuard(); Instant until = guard.blockedUntil("user_token"); // null 表示未被限流;否则为本地解禁时间 guard.clear("user_token"); // 例如:人工确认服务端已解禁后立即放行 ``` 需要完全关闭这个行为(不推荐): ```java PushPlusClient client = PushPlusClient.builder() .token("xxx") .rateLimitGuardEnabled(false) .build(); ``` 或者按业务把禁推时长拉长 / 缩短: ```java PushPlusClient client = PushPlusClient.builder() .token("xxx") .rateLimitCooldown(Duration.ofDays(2)) // 与文档示例的"2 天恢复正常"对齐 .build(); ``` ## 构建与发布 ```bash ./mvnw clean install # 本地安装 ./mvnw test # 跑单元测试 ./mvnw -pl perk-pushplus-sdk-example exec:java \ -Dexec.mainClass=example.com.perk.pushplus.QuickStart # 跑示例 ``` ## License Apache License 2.0