|
@@ -0,0 +1,233 @@
|
|
|
|
|
+package cn.iocoder.yudao.module.system.service.appnotify;
|
|
|
|
|
+
|
|
|
|
|
+import com.aliyun.auth.credentials.utils.ParameterHelper;
|
|
|
|
|
+import com.aliyuncs.DefaultAcsClient;
|
|
|
|
|
+import com.aliyuncs.IAcsClient;
|
|
|
|
|
+import com.aliyuncs.exceptions.ClientException;
|
|
|
|
|
+import com.aliyuncs.exceptions.ServerException;
|
|
|
|
|
+import com.aliyuncs.profile.DefaultProfile;
|
|
|
|
|
+import com.aliyuncs.push.model.v20160801.PushRequest;
|
|
|
|
|
+import com.aliyuncs.push.model.v20160801.PushResponse;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+import org.springframework.validation.annotation.Validated;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.Date;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+import java.util.UUID;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * app消息发送 Service 实现类(修正Push类找不到的版本)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author xrcoder
|
|
|
|
|
+ */
|
|
|
|
|
+@Service
|
|
|
|
|
+@Validated
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+public class AppNotifySendServiceImpl implements AppNotifySendService {
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 配置项(放到application.yml中) ==========
|
|
|
|
|
+ /** 阿里云Push的AppKey */
|
|
|
|
|
+ @Value("${aliyun.push.app-key:}")
|
|
|
|
|
+ private Long appKey;
|
|
|
|
|
+
|
|
|
|
|
+ /** 阿里云AccessKey ID */
|
|
|
|
|
+ @Value("${aliyun.access-key-id:}")
|
|
|
|
|
+ private String accessKeyId;
|
|
|
|
|
+
|
|
|
|
|
+ /** 阿里云AccessKey Secret */
|
|
|
|
|
+ @Value("${aliyun.access-key-secret:}")
|
|
|
|
|
+ private String accessKeySecret;
|
|
|
|
|
+
|
|
|
|
|
+ /** 阿里云Push的地域ID(默认杭州) */
|
|
|
|
|
+ @Value("${aliyun.push.region-id:cn-hangzhou}")
|
|
|
|
|
+ private String regionId;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据用户ID发送单条App通知(核心业务方法)
|
|
|
|
|
+ */
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Long sendSingleAppNotifyToAdmin(Long userId, String templateCode, Map<String, Object> templateParams) {
|
|
|
|
|
+ // 1. 根据userId查询对应的设备ID(替换为你的实际查询逻辑)
|
|
|
|
|
+ String deviceId = getDeviceIdByUserId(userId);
|
|
|
|
|
+ if (deviceId.isEmpty()) {
|
|
|
|
|
+ log.warn("发送单用户App通知失败:用户{}未绑定设备ID", userId);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 解析模板标题和内容(替换为你的实际模板逻辑)
|
|
|
|
|
+ String pushTitle = parseTemplateTitle(templateCode, templateParams);
|
|
|
|
|
+ String pushBody = parseTemplateBody(templateCode, templateParams);
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 3. 创建阿里云客户端(修正后的方式,不会报找不到Push类)
|
|
|
|
|
+ IAcsClient client = createAcsClient();
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 构建单设备推送请求
|
|
|
|
|
+ PushRequest pushRequest = buildSingleDevicePushRequest(deviceId, pushTitle, pushBody);
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 发送推送
|
|
|
|
|
+ PushResponse response = client.getAcsResponse(pushRequest);
|
|
|
|
|
+
|
|
|
|
|
+ // 6. 解析返回结果
|
|
|
|
|
+ String requestId = response.getRequestId();
|
|
|
|
|
+ String messageId = response.getMessageId();
|
|
|
|
|
+ log.info("单用户App推送成功:userId={}, requestId={}, messageId={}", userId, requestId, messageId);
|
|
|
|
|
+
|
|
|
|
|
+ // 返回消息ID(转为Long,若messageId是字符串可自行调整)
|
|
|
|
|
+ return Long.parseLong(messageId);
|
|
|
|
|
+ } catch (ServerException e) {
|
|
|
|
|
+ log.error("单用户App推送失败(服务端错误):userId={}, templateCode={}", userId, templateCode, e);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ } catch (ClientException e) {
|
|
|
|
|
+ log.error("单用户App推送失败(客户端错误):userId={}, templateCode={}", userId, templateCode, e);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 通用推送方法(推送给全部用户)
|
|
|
|
|
+ */
|
|
|
|
|
+ public Boolean pushNotify() {
|
|
|
|
|
+ if (appKey == null || appKey == 0) {
|
|
|
|
|
+ log.error("App推送失败:未配置阿里云Push的appKey");
|
|
|
|
|
+ return Boolean.FALSE;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 创建客户端
|
|
|
|
|
+ IAcsClient client = createAcsClient();
|
|
|
|
|
+ // 构建全量推送请求
|
|
|
|
|
+ PushRequest pushRequest = buildAllDevicePushRequest("系统通知", "这是一条全量推送的测试通知");
|
|
|
|
|
+ // 发送推送
|
|
|
|
|
+ PushResponse response = client.getAcsResponse(pushRequest);
|
|
|
|
|
+
|
|
|
|
|
+ log.info("全量App推送成功:requestId={}, messageId={}", response.getRequestId(), response.getMessageId());
|
|
|
|
|
+ return Boolean.TRUE;
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("全量App推送失败", e);
|
|
|
|
|
+ return Boolean.FALSE;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 私有工具方法 ==========
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建阿里云ACS客户端(修正后的方式,解决Push类找不到的问题)
|
|
|
|
|
+ */
|
|
|
|
|
+ private IAcsClient createAcsClient() {
|
|
|
|
|
+ // 校验配置
|
|
|
|
|
+ if (accessKeyId.isEmpty() || accessKeySecret.isEmpty()) {
|
|
|
|
|
+ throw new IllegalArgumentException("阿里云AccessKey配置为空,请检查配置项");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 初始化阿里云配置
|
|
|
|
|
+ DefaultProfile profile = DefaultProfile.getProfile(
|
|
|
|
|
+ regionId, // 地域ID
|
|
|
|
|
+ accessKeyId, // AccessKey ID
|
|
|
|
|
+ accessKeySecret // AccessKey Secret
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 创建客户端(这是阿里云官方推荐的稳定方式)
|
|
|
|
|
+ return new DefaultAcsClient(profile);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 构建单设备推送请求
|
|
|
|
|
+ */
|
|
|
|
|
+ private PushRequest buildSingleDevicePushRequest(String deviceId, String title, String body) {
|
|
|
|
|
+ PushRequest request = new PushRequest();
|
|
|
|
|
+ // 基础配置
|
|
|
|
|
+ request.setAppKey(appKey);
|
|
|
|
|
+ request.setTarget("DEVICE"); // 按设备推送
|
|
|
|
|
+ request.setTargetValue(deviceId); // 设备ID
|
|
|
|
|
+ request.setDeviceType("ALL"); // 支持Android+iOS
|
|
|
|
|
+ request.setPushType("NOTICE"); // 通知类型
|
|
|
|
|
+ request.setTitle(title);
|
|
|
|
|
+ request.setBody(body);
|
|
|
|
|
+
|
|
|
|
|
+ // iOS配置
|
|
|
|
|
+ request.setIOSBadge(1);
|
|
|
|
|
+ request.setIOSApnsEnv("PRODUCT");
|
|
|
|
|
+ request.setIOSRemind(true);
|
|
|
|
|
+ request.setIOSExtParameters("{\"type\":\"single\"}");
|
|
|
|
|
+
|
|
|
|
|
+ // Android配置
|
|
|
|
|
+ request.setAndroidOpenType("ACTIVITY");
|
|
|
|
|
+ request.setAndroidNotifyType("SOUND");
|
|
|
|
|
+ request.setAndroidExtParameters("{\"type\":\"single\"}");
|
|
|
|
|
+
|
|
|
|
|
+ // 推送时间(立即推送)
|
|
|
|
|
+ request.setPushTime(ParameterHelper.getISO8601Time(new Date()));
|
|
|
|
|
+ // 离线存储(12小时过期)
|
|
|
|
|
+ request.setStoreOffline(true);
|
|
|
|
|
+ request.setExpireTime(ParameterHelper.getISO8601Time(new Date(System.currentTimeMillis() + 12 * 3600 * 1000)));
|
|
|
|
|
+
|
|
|
|
|
+ return request;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 构建全量推送请求
|
|
|
|
|
+ */
|
|
|
|
|
+ private PushRequest buildAllDevicePushRequest(String title, String body) {
|
|
|
|
|
+ PushRequest request = new PushRequest();
|
|
|
|
|
+ // 基础配置
|
|
|
|
|
+ request.setAppKey(appKey);
|
|
|
|
|
+ request.setTarget("ALL");
|
|
|
|
|
+ request.setTargetValue("all");
|
|
|
|
|
+ request.setDeviceType("ALL");
|
|
|
|
|
+ request.setPushType("NOTICE");
|
|
|
|
|
+ request.setTitle(title);
|
|
|
|
|
+ request.setBody(body);
|
|
|
|
|
+
|
|
|
|
|
+ // iOS配置
|
|
|
|
|
+ request.setIOSBadge(5);
|
|
|
|
|
+ request.setIOSMusic("default");
|
|
|
|
|
+ request.setIOSApnsEnv("PRODUCT");
|
|
|
|
|
+ request.setIOSRemind(true);
|
|
|
|
|
+ request.setIOSRemindBody(body);
|
|
|
|
|
+ request.setIOSExtParameters("{\"k1\":\"ios\",\"k2\":\"v2\"}");
|
|
|
|
|
+
|
|
|
|
|
+ // Android配置
|
|
|
|
|
+ request.setAndroidOpenType("ACTIVITY");
|
|
|
|
|
+ request.setAndroidNotifyType("SOUND");
|
|
|
|
|
+ request.setAndroidOpenUrl("http://www.alibaba.com");
|
|
|
|
|
+ request.setAndroidMusic("alicloud_notification_sound");
|
|
|
|
|
+ request.setAndroidActivity("com.alibaba.push.PushActivity");
|
|
|
|
|
+ request.setAndroidPopupActivity("com.alibaba.push.PopupActivity");
|
|
|
|
|
+ request.setAndroidPopupTitle("系统通知");
|
|
|
|
|
+ request.setAndroidPopupBody(body);
|
|
|
|
|
+ request.setAndroidExtParameters("{\"k1\":\"android\",\"k2\":\"v2\"}");
|
|
|
|
|
+
|
|
|
|
|
+ // 推送时间(1小时后)
|
|
|
|
|
+ request.setPushTime(ParameterHelper.getISO8601Time(new Date(System.currentTimeMillis() + 3600 * 1000)));
|
|
|
|
|
+ // 离线存储
|
|
|
|
|
+ request.setStoreOffline(true);
|
|
|
|
|
+ request.setExpireTime(ParameterHelper.getISO8601Time(new Date(System.currentTimeMillis() + 12 * 3600 * 1000)));
|
|
|
|
|
+
|
|
|
|
|
+ return request;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据用户ID获取设备ID(替换为你的实际逻辑)
|
|
|
|
|
+ */
|
|
|
|
|
+ private String getDeviceIdByUserId(Long userId) {
|
|
|
|
|
+ // 示例:模拟返回设备ID
|
|
|
|
|
+ return "DEVICE" + UUID.randomUUID().toString().replace("-", "");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 解析模板标题(替换为你的实际逻辑)
|
|
|
|
|
+ */
|
|
|
|
|
+ private String parseTemplateTitle(String templateCode, Map<String, Object> params) {
|
|
|
|
|
+ return "【系统通知】" + params.get("titleSuffix");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 解析模板内容(替换为你的实际逻辑)
|
|
|
|
|
+ */
|
|
|
|
|
+ private String parseTemplateBody(String templateCode, Map<String, Object> params) {
|
|
|
|
|
+ return "您有一条新的通知:" + params.get("content");
|
|
|
|
|
+ }
|
|
|
|
|
+}
|