From 19abeede3dd12a6abb42292f38cea918e6198428 Mon Sep 17 00:00:00 2001 From: LiGang <98359@runjian.com> Date: Mon, 13 Nov 2023 22:34:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8F=91=E9=80=81=E5=B0=8F?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E8=AE=A2=E9=98=85=E6=B6=88=E6=81=AF=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/imdroid/beidou/auth/SessionUtils.java | 6 + .../com/imdroid/beidou/auth/WebMvcConfig.java | 3 +- .../beidou/config/WxMiniProperties.java | 27 ++--- .../controller/MiniProgramController.java | 48 ++++++++ .../beidou/controller/OssFileController.java | 7 +- .../dto/MiniHardwareDeviceMessageDTO.java | 65 +++++++++++ .../beidou/data/dto/MiniSnTicketBodyDTO.java | 31 +++++ .../data/dto/MiniSubscribeMessageDTO.java | 56 +++++++++ .../beidou/data/vo/mini/DeviceSnTicketVO.java | 25 ++++ .../com/imdroid/beidou/utils/GsonUtils.java | 76 +++++++++++++ .../com/imdroid/beidou/utils/WXUtils.java | 107 +++++++++++++++++- .../src/main/resources/application.properties | 2 + 12 files changed, 432 insertions(+), 21 deletions(-) create mode 100644 sec-beidou/src/main/java/com/imdroid/beidou/controller/MiniProgramController.java create mode 100644 sec-beidou/src/main/java/com/imdroid/beidou/data/dto/MiniHardwareDeviceMessageDTO.java create mode 100644 sec-beidou/src/main/java/com/imdroid/beidou/data/dto/MiniSnTicketBodyDTO.java create mode 100644 sec-beidou/src/main/java/com/imdroid/beidou/data/dto/MiniSubscribeMessageDTO.java create mode 100644 sec-beidou/src/main/java/com/imdroid/beidou/data/vo/mini/DeviceSnTicketVO.java create mode 100644 sec-beidou/src/main/java/com/imdroid/beidou/utils/GsonUtils.java diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/auth/SessionUtils.java b/sec-beidou/src/main/java/com/imdroid/beidou/auth/SessionUtils.java index c3e79c4d..8081b782 100644 --- a/sec-beidou/src/main/java/com/imdroid/beidou/auth/SessionUtils.java +++ b/sec-beidou/src/main/java/com/imdroid/beidou/auth/SessionUtils.java @@ -13,6 +13,7 @@ import javax.servlet.http.HttpSession; */ public class SessionUtils { + public static final String SESSION_CURRENT_USER_ID = "user_id"; public static final String SESSION_CURRENT_USER = "login_user"; public static final String SESSION_TENANT_ID = "tenant_id"; public static final String SESSION_TENANT_NAME = "tenant_name"; @@ -28,6 +29,7 @@ public class SessionUtils { session.setAttribute(SESSION_TENANT_ID, user.getTenant_id()); session.setAttribute(SESSION_TENANT_NAME, tenant.getName()); session.setAttribute(SESSION_ROLE, user.getRole()); + session.setAttribute(SESSION_CURRENT_USER_ID, user.getId()); } public static Integer getTenantId(HttpServletRequest request) { @@ -37,4 +39,8 @@ public class SessionUtils { public static String getRole(HttpServletRequest request) { return (String) request.getSession().getAttribute(SESSION_ROLE); } + + public static Long getUserId(HttpServletRequest request) { + return (Long) request.getSession().getAttribute(SESSION_CURRENT_USER_ID); + } } diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/auth/WebMvcConfig.java b/sec-beidou/src/main/java/com/imdroid/beidou/auth/WebMvcConfig.java index 8dab760a..875dfccd 100644 --- a/sec-beidou/src/main/java/com/imdroid/beidou/auth/WebMvcConfig.java +++ b/sec-beidou/src/main/java/com/imdroid/beidou/auth/WebMvcConfig.java @@ -21,6 +21,7 @@ public class WebMvcConfig implements WebMvcConfigurer { public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor) .addPathPatterns("/**") - .excludePathPatterns("/**/*.js","/**/*.css","/**/*.jpg","/**/*.png", "/do_login", "/mini-register", "/login", "/test/**", "/api/**"); + .excludePathPatterns("/**/*.js","/**/*.css","/**/*.jpg","/**/*.png", "/do_login", "/mini-register", "/login", "/test/**", "/api/**", + "/file/upload"); } } diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/config/WxMiniProperties.java b/sec-beidou/src/main/java/com/imdroid/beidou/config/WxMiniProperties.java index e7117b4f..826eb0e9 100644 --- a/sec-beidou/src/main/java/com/imdroid/beidou/config/WxMiniProperties.java +++ b/sec-beidou/src/main/java/com/imdroid/beidou/config/WxMiniProperties.java @@ -1,5 +1,7 @@ package com.imdroid.beidou.config; +import lombok.Getter; +import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @@ -8,6 +10,8 @@ import org.springframework.context.annotation.Configuration; * * @author LiGang */ +@Setter +@Getter @Configuration @ConfigurationProperties("app.bis") public class WxMiniProperties { @@ -16,21 +20,14 @@ public class WxMiniProperties { private String secret; + /** + * 设备型号id + */ + private String modelId; - public String getAppid() { - return appid; - } - - public void setAppid(String appid) { - this.appid = appid; - } - - public String getSecret() { - return secret; - } - - public void setSecret(String secret) { - this.secret = secret; - } + /** + * 设备告警模板消息id + */ + private String deviceAlertTemplateId; } diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/controller/MiniProgramController.java b/sec-beidou/src/main/java/com/imdroid/beidou/controller/MiniProgramController.java new file mode 100644 index 00000000..591798fa --- /dev/null +++ b/sec-beidou/src/main/java/com/imdroid/beidou/controller/MiniProgramController.java @@ -0,0 +1,48 @@ +package com.imdroid.beidou.controller; + +import com.imdroid.beidou.auth.SessionUtils; +import com.imdroid.beidou.common.HttpResult; +import com.imdroid.beidou.config.WxMiniProperties; +import com.imdroid.beidou.data.dto.MiniSnTicketBodyDTO; +import com.imdroid.beidou.data.vo.mini.DeviceSnTicketVO; +import com.imdroid.beidou.utils.WXUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +/** + * 微信小程序相关API调用控制器 + * + * @author LiGang + * @date 2023/11/12 10:38 + */ +@RestController +@RequestMapping("/mini") +@RequiredArgsConstructor +public class MiniProgramController { + + private final WxMiniProperties wxMiniProperties; + private final WXUtils wxUtils; + + /** + * 获取设备snTicket + * + * @param request the http servlet request + */ + @GetMapping(value = "/getsnticket") + public HttpResult getSnTicket(HttpServletRequest request) { + Long userId = SessionUtils.getUserId(request); + if (userId == null) { + return HttpResult.fail("用户id为空"); + } + MiniSnTicketBodyDTO snTicketBodyDTO = new MiniSnTicketBodyDTO(String.valueOf(userId), wxMiniProperties.getModelId()); + String snTicket = wxUtils.getSnTicket(snTicketBodyDTO); + if (snTicket == null) { + return HttpResult.fail("获取snTicket失败"); + } + return HttpResult.success(new DeviceSnTicketVO(String.valueOf(userId), snTicket, wxMiniProperties.getModelId())); + } +} diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/controller/OssFileController.java b/sec-beidou/src/main/java/com/imdroid/beidou/controller/OssFileController.java index 1af77adb..c0daf47c 100644 --- a/sec-beidou/src/main/java/com/imdroid/beidou/controller/OssFileController.java +++ b/sec-beidou/src/main/java/com/imdroid/beidou/controller/OssFileController.java @@ -33,7 +33,12 @@ public class OssFileController { */ @RequestMapping("/upload") public HttpResult upload(@RequestParam("file") MultipartFile multipartFile) throws Exception { - String filename = multipartFile.getOriginalFilename(); + String originFilename = multipartFile.getOriginalFilename(); + if (originFilename == null) { + originFilename = ".file"; + } + String suffix = originFilename.substring(originFilename.lastIndexOf(".")); + String filename = UUID.randomUUID().toString().replace("-", "") + suffix; String url = aliyunOssUtils.uploadFile(multipartFile, filename, "common"); return HttpResult.success(url); } diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/data/dto/MiniHardwareDeviceMessageDTO.java b/sec-beidou/src/main/java/com/imdroid/beidou/data/dto/MiniHardwareDeviceMessageDTO.java new file mode 100644 index 00000000..fd49f761 --- /dev/null +++ b/sec-beidou/src/main/java/com/imdroid/beidou/data/dto/MiniHardwareDeviceMessageDTO.java @@ -0,0 +1,65 @@ +package com.imdroid.beidou.data.dto; + +import com.google.gson.annotations.Expose; +import com.imdroid.beidou.utils.GsonUtils; +import lombok.Getter; +import lombok.Setter; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 小程序硬件设备消息body对象 + * + * @author LiGang + * @date 2023/11/12 10:19 + */ +@Setter +@Getter +public class MiniHardwareDeviceMessageDTO { + + @Expose + private List to_openid_list; + + @Expose + private String template_id; + + @Expose + private String sn; + + @Expose + private String modelId; + + @Expose + private String page; + + @Expose + private String miniprogram_state = "developer"; + + @Expose + private String lang = "zh_CN"; + + @Expose + private Map> data; + + + public void addDataItem(String key, Object value) { + if (data == null) { + data = new HashMap<>(8); + } + Map valueMap = new HashMap<>(1); + valueMap.put("value", value); + data.put(key, valueMap); + } + + public void removeDataItem(String key) { + data.remove(key); + } + + @Override + public String toString() { + return GsonUtils.toJsonExpose(this); + } + +} diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/data/dto/MiniSnTicketBodyDTO.java b/sec-beidou/src/main/java/com/imdroid/beidou/data/dto/MiniSnTicketBodyDTO.java new file mode 100644 index 00000000..a29a6833 --- /dev/null +++ b/sec-beidou/src/main/java/com/imdroid/beidou/data/dto/MiniSnTicketBodyDTO.java @@ -0,0 +1,31 @@ +package com.imdroid.beidou.data.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * 小程序请求snTicket的body参数对象 + * + * @author LiGang + * @date 2023/11/12 10:14 + */ +@Setter +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class MiniSnTicketBodyDTO { + + /** + * sn 设备唯一序列号。由厂商分配,长度不能超过128字节。字符只接受数字,大小写字母,下划线(_)和连字符(-) + * 本程序可使用用户id,来实现用户接收多个设备的消息 + */ + private String sn; + + /** + * 设备型号 id ,通过注册设备获得 + */ + private String model_id; + +} diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/data/dto/MiniSubscribeMessageDTO.java b/sec-beidou/src/main/java/com/imdroid/beidou/data/dto/MiniSubscribeMessageDTO.java new file mode 100644 index 00000000..ff6cf2ae --- /dev/null +++ b/sec-beidou/src/main/java/com/imdroid/beidou/data/dto/MiniSubscribeMessageDTO.java @@ -0,0 +1,56 @@ +package com.imdroid.beidou.data.dto; + +import com.google.gson.annotations.Expose; +import com.imdroid.beidou.utils.GsonUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.HashMap; +import java.util.Map; + +/** + * 小程序订阅消息 + * + * @author LiGang + * @date 2023/11/11 16:09 + */ +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class MiniSubscribeMessageDTO { + + @Expose + private String touser; + + @Expose + private String template_id; + + @Expose + private String page; + + @Expose + private String miniprogram_state = "developer"; + + @Expose + private String lang = "zh_CN"; + + @Expose + private Map> data; + + + public void addDataItem(String key, Object value) { + if (data == null) { + data = new HashMap<>(8); + } + Map valueMap = new HashMap<>(1); + valueMap.put("value", value); + data.put(key, valueMap); + } + + public void removeDataItem(String key) { + data.remove(key); + } +} diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/data/vo/mini/DeviceSnTicketVO.java b/sec-beidou/src/main/java/com/imdroid/beidou/data/vo/mini/DeviceSnTicketVO.java new file mode 100644 index 00000000..ec448014 --- /dev/null +++ b/sec-beidou/src/main/java/com/imdroid/beidou/data/vo/mini/DeviceSnTicketVO.java @@ -0,0 +1,25 @@ +package com.imdroid.beidou.data.vo.mini; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * 小程序设备SnTicket VO类 + * + * @author LiGang + * @date 2023/11/12 11:10 + */ +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class DeviceSnTicketVO { + + private String sn; + + private String snTicket; + + private String modelId; +} diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/utils/GsonUtils.java b/sec-beidou/src/main/java/com/imdroid/beidou/utils/GsonUtils.java new file mode 100644 index 00000000..a438f9a3 --- /dev/null +++ b/sec-beidou/src/main/java/com/imdroid/beidou/utils/GsonUtils.java @@ -0,0 +1,76 @@ +package com.imdroid.beidou.utils; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.lang.reflect.Type; + +/** + * @author Layton + * @date 2022/3/28 15:35 + */ +public class GsonUtils { + + private static final Gson gson = new Gson(); + + private static final Gson serializeNullGson = new GsonBuilder().serializeNulls().disableHtmlEscaping().create(); + + private static final Gson serializeExposeGson = new GsonBuilder().serializeNulls().excludeFieldsWithoutExposeAnnotation().create(); + + public static String toJsonSerializeNull(Object c) { + return serializeNullGson.toJson(c); + } + + public static String toJson(Object c) { + return gson.toJson(c); + } + + public static String toJsonExpose(Object c) { + return serializeExposeGson.toJson(c); + } + + public static T fromJson(String json, Class clz) { + return gson.fromJson(json, clz); + } + + public static T fromJson(String json, Type typeOfT) { + return gson.fromJson(json, typeOfT); + } + + public static String getValue(JsonObject jsonObject, String key) { + JsonElement element = jsonObject.has(key) ? jsonObject.get(key) : null; + if (element == null) { + return ""; + } + if (element.isJsonObject()) { + return element.getAsJsonObject().toString(); + } else if (element.isJsonNull()) { + return ""; + } else if (element.isJsonArray()) { + return element.getAsJsonArray().toString(); + } else { + if (element.getAsJsonPrimitive().isBoolean()) { + return element.getAsBoolean() ? "true" : "false"; + } else { + return element.getAsString(); + } + } + } + + public static Number getNumber(JsonElement element) { ; + if (element == null) { + return null; + } + if (element.isJsonNull()) { + return null; + } else { + try { + return element.getAsNumber(); + } catch (Exception e) { + return null; + } + } + } +} diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/utils/WXUtils.java b/sec-beidou/src/main/java/com/imdroid/beidou/utils/WXUtils.java index 1412e92a..b689a523 100644 --- a/sec-beidou/src/main/java/com/imdroid/beidou/utils/WXUtils.java +++ b/sec-beidou/src/main/java/com/imdroid/beidou/utils/WXUtils.java @@ -5,11 +5,14 @@ import com.imdroid.beidou.config.WxMiniProperties; import com.imdroid.beidou.data.WxMiniUserSession; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.imdroid.beidou.data.dto.MiniHardwareDeviceMessageDTO; +import com.imdroid.beidou.data.dto.MiniSnTicketBodyDTO; +import com.imdroid.beidou.data.dto.MiniSubscribeMessageDTO; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** - * 描述 + * 微信相关工具类 * * @author LiGang */ @@ -23,24 +26,120 @@ public class WXUtils { this.wxMiniProperties = wxMiniProperties; } + private String accessToken; + private long accessTokenExpireTime = 0L; + private static final long PRE_EXPIRE_TIME = 5 * 60 * 1000L; + /** - * 微信api地址 + * 微信小程序登录api地址 */ protected final String API_URL = "https://api.weixin.qq.com/sns/jscode2session"; + protected final String API_SEND_SUBSCRIBE_MESSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send"; + + protected final String API_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token"; + + protected final String API_SN_TICKET_URL = "https://api.weixin.qq.com/wxa/getsnticket"; + + protected final String API_HARDWARE_DEVICE_MESSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/device/subscribe/send"; + + /** + * 获取小程序 access_token + * + * @return access_token + */ + public String getAccessToken() { + if (accessTokenExpireTime - System.currentTimeMillis() >= PRE_EXPIRE_TIME ) { + return accessToken; + } + String url = API_ACCESS_TOKEN_URL + "?grant_type=client_credential&appid=" + wxMiniProperties.getAppid() + + "&secret=" + wxMiniProperties.getSecret(); + try { + final String response = HttpUtil.doGet(url); + log.info("小程序获取access_token结果:{}", response); + JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject(); + accessTokenExpireTime = System.currentTimeMillis() + 7200 * 1000; + accessToken = jsonObject.get("access_token").getAsString(); + return accessToken; + } catch (Exception e) { + log.error("小程序登录结果:{}", e.toString()); + return null; + } + } + + /** + * 获取小程序用户的openid + * + * @param code 登录码 + * @return 小程序用户的openid + */ public WxMiniUserSession getMiniProgramUserOpenid(String code) { - String requestURL = String.format(API_URL + "?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code" + String requestUrl = String.format(API_URL + "?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code" , wxMiniProperties.getAppid(), wxMiniProperties.getSecret(), code); try { - final String response = HttpUtil.doGet(requestURL); + final String response = HttpUtil.doGet(requestUrl); log.info("小程序登录结果:{}", response); JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject(); String sessionKey = jsonObject.get("session_key").getAsString(); String openid = jsonObject.get("openid").getAsString(); return new WxMiniUserSession(sessionKey, openid); } catch (Exception e) { + log.error("小程序登录结果:{}", e.toString()); return null; } } + + /** + * 发送小程序订阅消息 + * + * @param miniSubscribeMessageDTO 小程序订阅消息对象 + */ + public void sendSubscribeMessage(MiniSubscribeMessageDTO miniSubscribeMessageDTO) { + String requestUrl = API_SEND_SUBSCRIBE_MESSAGE_URL + "?access_token=" + getAccessToken(); + try { + String dataJson = GsonUtils.toJson(miniSubscribeMessageDTO); + final String response = HttpUtil.doPostJson(requestUrl, dataJson); + log.info("发送订阅消息结果:{}", response); + } catch (Exception e) { + log.error("发送订阅消息结果:{}",e.toString()); + } + } + + /** + * 获取设备snTicket + * + * @param miniSnTicketBodyDTO MiniSnTicketBodyDTO + * @return 设备snTicket + */ + public String getSnTicket(MiniSnTicketBodyDTO miniSnTicketBodyDTO) { + String requestUrl = API_SN_TICKET_URL + "?access_token=" + getAccessToken(); + try { + String dataJson = GsonUtils.toJson(miniSnTicketBodyDTO); + final String response = HttpUtil.doPostJson(requestUrl, dataJson); + log.info("获取设备snTicket结果:{}", response); + JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject(); + return jsonObject.get("sn_ticket").getAsString(); + } catch (Exception e) { + log.error("获取设备snTicket结果:{}",e.toString()); + } + return null; + } + + /** + * 发送设备消息 + * + * @param hardwareDeviceMessageDTO MiniHardwareDeviceMessageDTO + */ + public void sendHardwareDeviceMessage(MiniHardwareDeviceMessageDTO hardwareDeviceMessageDTO) { + String requestUrl = API_HARDWARE_DEVICE_MESSAGE_URL + "?access_token=" + getAccessToken(); + try { + String dataJson = GsonUtils.toJson(hardwareDeviceMessageDTO); + final String response = HttpUtil.doPostJson(requestUrl, dataJson); + log.info("发送设备消息结果:{}", response); + } catch (Exception e) { + log.error("发送设备消息结果: {}", e.toString()); + } + } + } diff --git a/sec-beidou/src/main/resources/application.properties b/sec-beidou/src/main/resources/application.properties index bb5aa987..d12929f3 100644 --- a/sec-beidou/src/main/resources/application.properties +++ b/sec-beidou/src/main/resources/application.properties @@ -34,6 +34,8 @@ default_pwd = 666666 # miniprogram info app.bis.appid = wxb51f83011ecef07f app.bis.secret = fa046f918a7a10fe6f21c6ccfc4ea918 +app.bis.modelId = askdf +app.bis.device-alert-template-id = asdf aliyun.oss.endpoint = https://oss-cn-shanghai.aliyuncs.com aliyun.oss.accessKey = LTAI4G6hmpX7h9hQvUnwKWxj