socket 消息收发

master
guochaojie 5 months ago
parent 17cbf7a390
commit 1ca0755341

@ -58,6 +58,10 @@
<artifactId>mybatis-plus-join-core</artifactId>
<version>1.4.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
<build>

@ -50,7 +50,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
"/upload/**",
"/qrcode/**.txt",
"/wx-message/**",//微信消息推送验证
"/wx-message/**",//微信消息推送验证
"/webSocketServer/**",//socket通信
"/doc.html"
);

@ -0,0 +1,22 @@
package cc.yunxi.config;
import cc.yunxi.utils.WsSocketHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import javax.annotation.Resource;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Resource
private WsSocketHandler wsSocketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(wsSocketHandler, "/webSocketServer").setAllowedOrigins("*");
}
}

@ -2,10 +2,10 @@ package cc.yunxi.controller;
import cc.yunxi.common.domain.CommonResult;
import cc.yunxi.domain.vo.vxmessage.AccessToken;
import cc.yunxi.domain.vo.vxmessage.MessageTemplate;
import cc.yunxi.domain.vo.vxmessage.OrderNew;
import cc.yunxi.domain.vo.vxmessage.ResultVo;
import cc.yunxi.domain.vo.socket.MessageTypeEnum;
import cc.yunxi.domain.vo.socket.SocketMessage;
import cc.yunxi.domain.vo.vxmessage.*;
import cc.yunxi.service.IWsService;
import cc.yunxi.utils.VerifyUtil;
import cc.yunxi.utils.WeChatMessageUtil;
import cc.yunxi.utils.WeChatUtil;
@ -13,9 +13,11 @@ import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.io.IOException;
@Api(tags = "对接微信推送认证")
@RestController
@ -26,7 +28,8 @@ public class WxMessageController {
@Resource
private WeChatMessageUtil weChatMessageUtil;
@Resource
private IWsService wsService;
@Resource
private VerifyUtil verifyUtil;
@ -87,7 +90,7 @@ public class WxMessageController {
JSONObject _encryptedData = JSONUtil.parseObj(encryptedData);
String encrypt = _encryptedData.getStr("Encrypt");
log.info("微信推送验证");
boolean isOfficial = verifyUtil.checkSignature("client", timestamp, nonce, encrypt,msgSignature);
boolean isOfficial = verifyUtil.checkSignature("client", timestamp, nonce, encrypt, msgSignature);
if (!isOfficial) {
log.error("接收推送消息失败!非官方推送,{},{},{},{}", timestamp, nonce, encrypt, msgSignature);
return "error";
@ -109,7 +112,7 @@ public class WxMessageController {
JSONObject _encryptedData = JSONUtil.parseObj(encryptedData);
String encrypt = _encryptedData.getStr("Encrypt");
log.info("微信推送验证");
boolean isOfficial = verifyUtil.checkSignature("recycler", timestamp, nonce, encrypt,msgSignature);
boolean isOfficial = verifyUtil.checkSignature("recycler", timestamp, nonce, encrypt, msgSignature);
if (!isOfficial) {
log.error("接收推送消息失败!非官方推送,{},{},{},{}", timestamp, nonce, encrypt, msgSignature);
return "error";
@ -123,29 +126,40 @@ public class WxMessageController {
@PostMapping("/getToken")
public CommonResult<AccessToken> getToken(@RequestBody String message) {
AccessToken client = weChatMessageUtil.getAccessToken("client");
SocketMessage sms = new SocketMessage();
sms.setMessageType(MessageTypeEnum.BOOKING);
sms.setGoodsType("塑料");
sms.setOrderNo("123332");
sms.setOrderAddress("地址");
sms.setOrderPhone("电话");
sms.setOrderTime("2024-06-04 17:00~18:00");
try {
wsService.sendMsgToUser("你好",sms);
} catch (IOException e) {
log.error("发送消息失败");
e.printStackTrace();
}
return CommonResult.success(client);
}
@PostMapping("/send")
public CommonResult<ResultVo> sendMessage(@RequestBody String message) {
AccessToken client = weChatMessageUtil.getAccessToken("client");
OrderNew orderNew = new OrderNew();
orderNew.setCharacter_string22("11");//订单号
orderNew.setTime1("2024/06/04 16:30~17:30");//预约时间
orderNew.setName15("上门回收");//服务名称
orderNew.setThing9("地址");//预约地址
orderNew.setPhone_number43("13183060802");//联系电话
orderNew.setCharacter_string22(new ValueTemplate("RO2024032815055294242"));//订单号
orderNew.setTime1(new ValueTemplate("2024/06/04 16:30~17:30"));//预约时间
orderNew.setName15(new ValueTemplate("预约上门回收"));//服务名称
orderNew.setThing9(new ValueTemplate("河南"));//预约地址
orderNew.setPhone_number43(new ValueTemplate("13183060802"));//联系电话
MessageTemplate template = new MessageTemplate();
template.setTemplate_id(OrderNew.TEMPLATE_ID);
template.setData(orderNew);
template.setTouser("oYkV866Jjz197Iya3kJQwdypNPq8");
template.setMiniprogram_state("trial");
template.setPage("/pages/orderDetail/orderDetail");
// template.setMiniprogram_state("trial");
template.setPage("/pages/orderDetail/orderDetail?orderId=RO2024032815055294242");
template.setLang("zh_CN");
ResultVo result = weChatMessageUtil.sendMessage(template, "client");
log.info(result.getErrcode() + "", result.getErrmsg());
return CommonResult.success(result);
}

@ -0,0 +1,22 @@
package cc.yunxi.domain.vo.socket;
public enum MessageTypeEnum {
BOOKING("新订单"),
TAKEN("已接单"),
FINISH("已完成"),
CANCEL("已取消");
private final String status;
MessageTypeEnum(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
@Override
public String toString() {
return this.getStatus();
}
}

@ -0,0 +1,42 @@
package cc.yunxi.domain.vo.socket;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("socket消息体")
public class SocketMessage {
@ApiModelProperty("消息类型")
private MessageTypeEnum messageType;
@ApiModelProperty("订单号")
private String orderNo;
@ApiModelProperty("订单状态")
private String orderStatus;
@ApiModelProperty("商品类型")
private String goodsType;
@ApiModelProperty("预约时间")
private String orderTime;
@ApiModelProperty("预约人号码")
private String orderPhone;
@ApiModelProperty("预约地址")
private String orderAddress;
@ApiModelProperty("预计重量")
private String orderWeight;
@ApiModelProperty("接单人")
private String takeOrderUser;
@ApiModelProperty("接单时间")
private String takeOrderTime;
@ApiModelProperty("接单人电话")
private String takeUserPhone;
@ApiModelProperty("完成时间")
private String finishTime;
@ApiModelProperty("实际金额")
private String realMoney;
}

@ -1,5 +1,6 @@
package cc.yunxi.domain.vo.vxmessage;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ -7,15 +8,17 @@ import lombok.Data;
@Data
@ApiModel("订单完成")
public class FinishOrder extends BaseMessage{
@JsonIgnore//不在data属性中
@ApiModelProperty("消息模版ID")
public static final String TEMPLATE_ID = "ZYcDpdGc0fh6bIfDFLZgxFstauznvG7tS4ewfu4ZNeE";
@ApiModelProperty("订单号")
private String character_string5;
private ValueTemplate character_string5;
@ApiModelProperty("订单状态")
private String thing3;
private ValueTemplate thing3;
@ApiModelProperty("实付金额")
private String amount22;
private ValueTemplate amount22;
@ApiModelProperty("服务方")
private String thing10;
private ValueTemplate thing10;
@ApiModelProperty("完成时间")
private String date9;
private ValueTemplate date9;
}

@ -16,7 +16,7 @@ public class MessageTemplate {
@ApiModelProperty("模板内容")
private BaseMessage data;
@ApiModelProperty("跳转小程序类型developer为开发版trial为体验版formal为正式版默认为正式版")
private String miniprogram_state;
private String miniprogram_state ="developer";
@ApiModelProperty("语言支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文)默认为zh_CN")
private String lang = "zh_CN";

@ -13,13 +13,13 @@ public class OrderNew extends BaseMessage{
@ApiModelProperty("消息模版ID")
public static final String TEMPLATE_ID = "TfLbZu3DxLSp4TnuTVCsDTY2U4zSV2M7MNTrGHaUL5s";
@ApiModelProperty("订单编号")
private String character_string22;
private ValueTemplate character_string22;
@ApiModelProperty("预约时间")
private String time1;
private ValueTemplate time1;
@ApiModelProperty("服务名称")
private String name15;
private ValueTemplate name15;
@ApiModelProperty("预约地址")
private String thing9;
private ValueTemplate thing9;
@ApiModelProperty("联系电话")
private String phone_number43;
private ValueTemplate phone_number43;
}

@ -1,5 +1,6 @@
package cc.yunxi.domain.vo.vxmessage;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
@ -8,14 +9,18 @@ import lombok.Data;
@ApiModel("已接单消息模板")
@Data
public class OrderTaken extends BaseMessage{
@JsonIgnore//不在data属性中
@ApiModelProperty("消息模版ID")
public static final String TEMPLATE_ID = "CGgIed4RMXLnsteWhj2l8h2R88OWAAJrLfPs5Uvyo5U";
@ApiModelProperty("订单号")
private String character_string5;
private ValueTemplate character_string5;
@ApiModelProperty("订单状态")
private String thing4;
private ValueTemplate thing4;
@ApiModelProperty("联系人")
private String thing18;
private ValueTemplate thing18;
@ApiModelProperty("联系电话")
private String phone_number14;
private ValueTemplate phone_number14;
@ApiModelProperty("服务时间")
private String thing6;
private ValueTemplate thing6;
}

@ -0,0 +1,14 @@
package cc.yunxi.domain.vo.vxmessage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
@ApiModel("值模板")
public class ValueTemplate {
@ApiModelProperty("值")
private String value;
}

@ -0,0 +1,35 @@
package cc.yunxi.service;
import cc.yunxi.domain.vo.socket.SocketMessage;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
/**
* ws
*/
public interface IWsService {
/**
*
*
* @param userinfo
* @param message
* @return
* @throws IOException
*/
public void sendMsgToUser(String userinfo, SocketMessage message) throws IOException;
/**
* 广
*
* @param text
* @return
* @throws IOException
*/
public void broadcastMsg(String text);
}

@ -0,0 +1,47 @@
package cc.yunxi.service.impl;
import cc.yunxi.domain.vo.socket.SocketMessage;
import cc.yunxi.service.IWsService;
import cc.yunxi.utils.WsSessionManager;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
@Service
@Slf4j
public class WsServiceImpl implements IWsService {
@Resource
private WsSessionManager sessionManager;
@Override
public void sendMsgToUser(String userinfo, SocketMessage message) throws IOException {
String text = JSONUtil.toJsonStr(message);
WebSocketSession userSession = sessionManager.getUserSession(userinfo);
if (userSession != null && userSession.isOpen()) {
userSession.sendMessage(new TextMessage(text));
}
}
@Override
public void broadcastMsg(String text) {
ConcurrentHashMap<String, WebSocketSession> allSession = sessionManager.getAllSession();
if (null != allSession && allSession.size() > 0) {
for (WebSocketSession session : allSession.values()) {
try {
if (null != session && session.isOpen()) {
session.sendMessage(new TextMessage(text));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

@ -76,17 +76,18 @@ public class WeChatMessageUtil {
if ("client".equals(endpointType)) {//预约端
param.put("appid", clientProperties.getAppId());
param.put("secret", clientProperties.getAppSecret());
result = HttpUtil.get(clientProperties.getTokenUrl(), param);
result = HttpUtil.get(clientProperties.getTokenUrl(), param,1000);
} else if ("recycler".equals(endpointType)) {//回收端
param.put("appid", recyclerProperties.getAppId());
param.put("secret", recyclerProperties.getAppSecret());
result = HttpUtil.get(recyclerProperties.getTokenUrl(), param);
result = HttpUtil.get(recyclerProperties.getTokenUrl(), param,1000);
} else {
return null;
}
AccessToken access_token = JSONUtil.toBean(result, AccessToken.class);
access_token.setExpires_in(new AtomicLong(10 * 1000 + access_token.getExpires_in().get() * 1000));
accessTokenMap.put(endpointType, access_token);
return access_token;
}
log.info("当前微信access_token为{}", token);
return token;

@ -0,0 +1,97 @@
package cc.yunxi.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class WsSessionManager {
/**
* session
*/
private static ConcurrentHashMap<String, WebSocketSession> SESSION_POOL = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String, WebSocketSession> USER_SESSION_POOL = new ConcurrentHashMap<>();
/**
* session
*
* @param key
*/
public void add(String key, WebSocketSession session) {
// 添加 session
SESSION_POOL.put(key, session);
}
public void addUserSession(String key, WebSocketSession session) {
//获取旧session
WebSocketSession oldSession = USER_SESSION_POOL.get(key);
if (null == oldSession //没有
|| !oldSession.getId().equals(session.getId()) //id不同
|| !oldSession.isOpen()) { //旧session已关闭
USER_SESSION_POOL.put(key, session); //则将新的session添加到池中
}
}
/**
* session, session
*
* @param key
* @return
*/
public WebSocketSession remove(String key) {
// 删除 session
USER_SESSION_POOL.remove(key);
return SESSION_POOL.remove(key);
}
/**
*
*
* @param key
*/
public void removeAndClose(String key) {
WebSocketSession session = remove(key);
if (session != null) {
try {
// 关闭连接
session.close();
} catch (IOException e) {
// todo: 关闭出现异常处理
e.printStackTrace();
}
}
}
/**
* session
*
* @param key
* @return
*/
public WebSocketSession getUserSession(String key) {
// 获得 session
return USER_SESSION_POOL.get(key);
}
/**
* session
*
* @param key
* @return
*/
public WebSocketSession getSession(String key) {
// 获得 session
return USER_SESSION_POOL.get(key);
}
//获取所有链接
public ConcurrentHashMap<String, WebSocketSession> getAllSession() {
return SESSION_POOL;
}
}

@ -0,0 +1,60 @@
package cc.yunxi.utils;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import javax.annotation.Resource;
import java.time.LocalDateTime;
/**
* ws
*/
@Component
@Slf4j
public class WsSocketHandler extends AbstractWebSocketHandler {
@Resource
private WsSessionManager SESSION_MANAGER;
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
log.info("建立ws连接");
SESSION_MANAGER.add(session.getId(), session);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
log.info("发送文本消息");
// 获得客户端传来的消息
String payload = message.getPayload();
log.info("server 接收到消息 " + payload);
JSONObject json = JSONUtil.parseObj(payload);
String userId = json.getStr("userId");
SESSION_MANAGER.addUserSession(userId, session);
session.sendMessage(new TextMessage("server 发送给的消息 " + payload + ",发送时间:" + LocalDateTime.now().toString()));
}
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
log.info("发送二进制消息");
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
log.error("异常处理");
SESSION_MANAGER.removeAndClose(session.getId());
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
log.info("关闭ws连接");
SESSION_MANAGER.removeAndClose(session.getId());
}
}

@ -104,6 +104,7 @@ nxhs:
- /api/device/**
- /api/qrcode/**.txt
- /api/wx-message/**
- /api/webSocketServer/**
adminKey: 8bd2aa89033ead51c505e44994e42189 # 后台接口访问Key
upload:
basePath: /upload/

Loading…
Cancel
Save