增加发送小程序订阅消息相关方法

This commit is contained in:
LiGang 2023-11-13 22:34:23 +08:00
parent 1a4736d91e
commit 19abeede3d
12 changed files with 432 additions and 21 deletions

View File

@ -13,6 +13,7 @@ import javax.servlet.http.HttpSession;
*/ */
public class SessionUtils { 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_CURRENT_USER = "login_user";
public static final String SESSION_TENANT_ID = "tenant_id"; public static final String SESSION_TENANT_ID = "tenant_id";
public static final String SESSION_TENANT_NAME = "tenant_name"; 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_ID, user.getTenant_id());
session.setAttribute(SESSION_TENANT_NAME, tenant.getName()); session.setAttribute(SESSION_TENANT_NAME, tenant.getName());
session.setAttribute(SESSION_ROLE, user.getRole()); session.setAttribute(SESSION_ROLE, user.getRole());
session.setAttribute(SESSION_CURRENT_USER_ID, user.getId());
} }
public static Integer getTenantId(HttpServletRequest request) { public static Integer getTenantId(HttpServletRequest request) {
@ -37,4 +39,8 @@ public class SessionUtils {
public static String getRole(HttpServletRequest request) { public static String getRole(HttpServletRequest request) {
return (String) request.getSession().getAttribute(SESSION_ROLE); return (String) request.getSession().getAttribute(SESSION_ROLE);
} }
public static Long getUserId(HttpServletRequest request) {
return (Long) request.getSession().getAttribute(SESSION_CURRENT_USER_ID);
}
} }

View File

@ -21,6 +21,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) { public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor) registry.addInterceptor(authInterceptor)
.addPathPatterns("/**") .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");
} }
} }

View File

@ -1,5 +1,7 @@
package com.imdroid.beidou.config; package com.imdroid.beidou.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -8,6 +10,8 @@ import org.springframework.context.annotation.Configuration;
* *
* @author LiGang * @author LiGang
*/ */
@Setter
@Getter
@Configuration @Configuration
@ConfigurationProperties("app.bis") @ConfigurationProperties("app.bis")
public class WxMiniProperties { public class WxMiniProperties {
@ -16,21 +20,14 @@ public class WxMiniProperties {
private String secret; private String secret;
/**
* 设备型号id
*/
private String modelId;
public String getAppid() { /**
return appid; * 设备告警模板消息id
} */
private String deviceAlertTemplateId;
public void setAppid(String appid) {
this.appid = appid;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
} }

View File

@ -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()));
}
}

View File

@ -33,7 +33,12 @@ public class OssFileController {
*/ */
@RequestMapping("/upload") @RequestMapping("/upload")
public HttpResult upload(@RequestParam("file") MultipartFile multipartFile) throws Exception { 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"); String url = aliyunOssUtils.uploadFile(multipartFile, filename, "common");
return HttpResult.success(url); return HttpResult.success(url);
} }

View File

@ -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<String> 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<String, Map<String, Object>> data;
public void addDataItem(String key, Object value) {
if (data == null) {
data = new HashMap<>(8);
}
Map<String, Object> 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);
}
}

View File

@ -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;
}

View File

@ -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<String, Map<String, Object>> data;
public void addDataItem(String key, Object value) {
if (data == null) {
data = new HashMap<>(8);
}
Map<String, Object> valueMap = new HashMap<>(1);
valueMap.put("value", value);
data.put(key, valueMap);
}
public void removeDataItem(String key) {
data.remove(key);
}
}

View File

@ -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;
}

View File

@ -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> T fromJson(String json, Class<T> clz) {
return gson.fromJson(json, clz);
}
public static <T> 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;
}
}
}
}

View File

@ -5,11 +5,14 @@ import com.imdroid.beidou.config.WxMiniProperties;
import com.imdroid.beidou.data.WxMiniUserSession; import com.imdroid.beidou.data.WxMiniUserSession;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; 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 lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* 描述 * 微信相关工具类
* *
* @author LiGang * @author LiGang
*/ */
@ -23,24 +26,120 @@ public class WXUtils {
this.wxMiniProperties = wxMiniProperties; 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_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) { 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); , wxMiniProperties.getAppid(), wxMiniProperties.getSecret(), code);
try { try {
final String response = HttpUtil.doGet(requestURL); final String response = HttpUtil.doGet(requestUrl);
log.info("小程序登录结果:{}", response); log.info("小程序登录结果:{}", response);
JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject(); JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject();
String sessionKey = jsonObject.get("session_key").getAsString(); String sessionKey = jsonObject.get("session_key").getAsString();
String openid = jsonObject.get("openid").getAsString(); String openid = jsonObject.get("openid").getAsString();
return new WxMiniUserSession(sessionKey, openid); return new WxMiniUserSession(sessionKey, openid);
} catch (Exception e) { } catch (Exception e) {
log.error("小程序登录结果:{}", e.toString());
return null; 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());
}
}
} }

View File

@ -34,6 +34,8 @@ default_pwd = 666666
# miniprogram info # miniprogram info
app.bis.appid = wxb51f83011ecef07f app.bis.appid = wxb51f83011ecef07f
app.bis.secret = fa046f918a7a10fe6f21c6ccfc4ea918 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.endpoint = https://oss-cn-shanghai.aliyuncs.com
aliyun.oss.accessKey = LTAI4G6hmpX7h9hQvUnwKWxj aliyun.oss.accessKey = LTAI4G6hmpX7h9hQvUnwKWxj