Merge pull request '优化客服模块' (#112) from sjy-two into master
All checks were successful
continuous-integration/drone Build is passing

Reviewed-on: #112
This commit is contained in:
root 2024-11-08 12:10:36 +08:00
commit c1a5340a73
15 changed files with 246 additions and 35 deletions

View File

@ -447,6 +447,8 @@
} }
await sendMessage(msg) await sendMessage(msg)
messages.success('转接成功') messages.success('转接成功')
conversation.value = null
} finally { } finally {
// todo // todo
} }

View File

@ -159,24 +159,87 @@
getRefreshToken() // 使 getRefreshToken() 使 getAccessToken() WebSocket 便访 getRefreshToken() // 使 getRefreshToken() 使 getAccessToken() WebSocket 便访
) // WebSocket ) // WebSocket
const chick = ref('2')
const userInfo = async () =>{ let a = 0;
chick.value = '1'
user.value = await UserApi.getUserInfo(userId.value) const {status, data, send, open, close} = useWebSocket(server.value, {
onConnected: function (ws) {
console.log('websocket 连接成功!', ws);
},
onDisconnected: function (ws, event) {
console.log('WebSocket 连接断开', event);
},
onError: function (ws, event) {
console.error('WebSocket 连接错误:', event);
if (event instanceof ErrorEvent) {
console.error('详细错误信息:', event.message);
} else {
console.error('非标准错误:', event);
} }
const zuoji = () =>{ },
chick.value = '2' onMessage: function (ws, event) {
// keFuChatBoxRef.value?.getNewMessageList(conversations.value) console.log('收到的 WebSocket 消息:', event.data);
memberBrowsingHistoryRef.value?.initHistory(conversations.value) a = a + 1 ;
if(a == 2){
getConversationList()
keFuChatBoxRef.value?.refreshMessageList()
a = 0;
} }
//
// if (event.data) {
// let parsedData;
// //
// try {
// parsedData = JSON.parse(event.data);
// } catch (error) {
// console.error('JSON :', error, ':', event.data);
// return; //
// }
/** 发起 WebSocket 连接 */ // // type
const {data, close, open} = useWebSocket(server.value, { // const { type } = parsedData;
autoReconnect: false, // if (!type) {
// console.warn(':', parsedData);
// return;
// }
// //
// switch (type) {
// case 'pong':
// //
// console.log(' ping ');
// // pong
// send(JSON.stringify({ type: 'pong' }));
// break;
// case 'chat':
// //
// console.log(':', parsedData.content);
// //
// break;
// case 'notification':
// //
// console.log(':', parsedData.message);
// //
// break;
// default:
// console.warn(':', type);
// break;
// }
// } else {
// console.warn(':', event);
// }
},
autoReconnect: false, //
heartbeat: true heartbeat: true
}) });
/** 监听 WebSocket 数据 */ /** 监听 WebSocket 数据 */
watchEffect(() => { watchEffect(() => {
@ -215,6 +278,8 @@
console.error(error) console.error(error)
} }
}) })
// ======================= WebSocket end ======================= // ======================= WebSocket end =======================
/** 加载会话列表 */ /** 加载会话列表 */
const keFuConversationRef = ref<InstanceType<typeof KeFuConversationList>>() const keFuConversationRef = ref<InstanceType<typeof KeFuConversationList>>()
@ -238,17 +303,31 @@
// window.location.href = '/kefu/support-staff'; // window.location.href = '/kefu/support-staff';
} }
const chick = ref('2')
const userInfo = async () =>{
chick.value = '1'
user.value = await UserApi.getUserInfo(userId.value)
}
const zuoji = () =>{
chick.value = '2'
// keFuChatBoxRef.value?.getNewMessageList(conversations.value)
memberBrowsingHistoryRef.value?.initHistory(conversations.value)
}
/** 初始化 */ /** 初始化 */
onMounted(() => { onMounted(() => {
getConversationList() getConversationList()
// websocket // websocket
open() open()
console.log('WebSocket 已初始化');
}) })
/** 销毁 */ /** 销毁 */
onBeforeUnmount(() => { onBeforeUnmount(() => {
// websocket // websocket
close() close()
console.log('WebSocket 已关闭');
}) })
</script> </script>

View File

@ -29,6 +29,6 @@ public class WebSocketProperties {
* 可选值localredisrocketmqkafkarabbitmq * 可选值localredisrocketmqkafkarabbitmq
*/ */
@NotNull(message = "WebSocket 的消息发送者不能为空") @NotNull(message = "WebSocket 的消息发送者不能为空")
private String senderType = "local"; private String senderType = "redis";
} }

View File

@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;
import cn.iocoder.yudao.framework.websocket.core.handler.JsonWebSocketMessageHandler; import cn.iocoder.yudao.framework.websocket.core.handler.JsonWebSocketMessageHandler;
import cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListener; import cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListener;
import cn.iocoder.yudao.framework.websocket.core.security.LoginUserHandshakeInterceptor; import cn.iocoder.yudao.framework.websocket.core.security.LoginUserHandshakeInterceptor;
import cn.iocoder.yudao.framework.websocket.core.security.WebSocketAuthorizeRequestsCustomizer;
import cn.iocoder.yudao.framework.websocket.core.sender.kafka.KafkaWebSocketMessageConsumer; import cn.iocoder.yudao.framework.websocket.core.sender.kafka.KafkaWebSocketMessageConsumer;
import cn.iocoder.yudao.framework.websocket.core.sender.kafka.KafkaWebSocketMessageSender; import cn.iocoder.yudao.framework.websocket.core.sender.kafka.KafkaWebSocketMessageSender;
import cn.iocoder.yudao.framework.websocket.core.sender.local.LocalWebSocketMessageSender; import cn.iocoder.yudao.framework.websocket.core.sender.local.LocalWebSocketMessageSender;
@ -76,10 +77,15 @@ public class YudaoWebSocketAutoConfiguration {
return new WebSocketSessionManagerImpl(); return new WebSocketSessionManagerImpl();
} }
@Bean
public WebSocketAuthorizeRequestsCustomizer webSocketAuthorizeRequestsCustomizer(WebSocketProperties webSocketProperties) {
return new WebSocketAuthorizeRequestsCustomizer(webSocketProperties);
}
// ==================== Sender 相关 ==================== // ==================== Sender 相关 ====================
@Configuration @Configuration
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "local", matchIfMissing = true) @ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "local")
public class LocalWebSocketMessageSenderConfiguration { public class LocalWebSocketMessageSenderConfiguration {
@Bean @Bean
@ -90,7 +96,7 @@ public class YudaoWebSocketAutoConfiguration {
} }
@Configuration @Configuration
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "redis", matchIfMissing = true) @ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "redis")
public class RedisWebSocketMessageSenderConfiguration { public class RedisWebSocketMessageSenderConfiguration {
@Bean @Bean
@ -108,7 +114,7 @@ public class YudaoWebSocketAutoConfiguration {
} }
@Configuration @Configuration
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "rocketmq", matchIfMissing = true) @ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "rocketmq")
public class RocketMQWebSocketMessageSenderConfiguration { public class RocketMQWebSocketMessageSenderConfiguration {
@Bean @Bean
@ -127,7 +133,7 @@ public class YudaoWebSocketAutoConfiguration {
} }
@Configuration @Configuration
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "rabbitmq", matchIfMissing = true) @ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "rabbitmq")
public class RabbitMQWebSocketMessageSenderConfiguration { public class RabbitMQWebSocketMessageSenderConfiguration {
@Bean @Bean
@ -156,7 +162,7 @@ public class YudaoWebSocketAutoConfiguration {
} }
@Configuration @Configuration
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "kafka", matchIfMissing = true) @ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "kafka")
public class KafkaWebSocketMessageSenderConfiguration { public class KafkaWebSocketMessageSenderConfiguration {
@Bean @Bean

View File

@ -43,7 +43,7 @@ public interface WebSocketSenderApi {
send(userType, userId, messageType, JsonUtils.toJsonString(messageContent)); send(userType, userId, messageType, JsonUtils.toJsonString(messageContent));
} }
default void sendObject(Integer userType, String messageType, Object messageContent) { default void sendObject(Integer userType, String messageType, Object messageContent) { //用户发送消息
send(userType, messageType, JsonUtils.toJsonString(messageContent)); send(userType, messageType, JsonUtils.toJsonString(messageContent));
} }

View File

@ -79,6 +79,12 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId> <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-system-biz</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -28,4 +28,8 @@ public class AppDiyTemplatePropertyRespVO {
@JsonRawValue @JsonRawValue
private String user; private String user;
private String goodsType;
private String themeType;
} }

View File

@ -61,4 +61,11 @@ public class DiyTemplateDO extends BaseDO {
*/ */
private String property; private String property;
@TableField(exist = false)
private String goodsType;
@TableField(exist = false)
private String themeType;
} }

View File

@ -11,12 +11,15 @@ import cn.iocoder.yudao.module.promotion.convert.diy.DiyPageConvert;
import cn.iocoder.yudao.module.promotion.convert.diy.DiyTemplateConvert; import cn.iocoder.yudao.module.promotion.convert.diy.DiyTemplateConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO; import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyTemplateMapper; import cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyTemplateMapper;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
@ -36,6 +39,9 @@ public class DiyTemplateServiceImpl implements DiyTemplateService {
@Resource @Resource
private DiyPageService diyPageService; private DiyPageService diyPageService;
@Resource
private DictDataApi dictDataApi;
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Override @Override
public Long createDiyTemplate(DiyTemplateCreateReqVO createReqVO) { public Long createDiyTemplate(DiyTemplateCreateReqVO createReqVO) {
@ -165,7 +171,17 @@ public class DiyTemplateServiceImpl implements DiyTemplateService {
@Override @Override
public DiyTemplateDO getUsedDiyTemplate() { public DiyTemplateDO getUsedDiyTemplate() {
return diyTemplateMapper.selectByUsed(true); DiyTemplateDO diyTemplateDO = diyTemplateMapper.selectByUsed(true);
List<DictDataRespDTO> dictDataList = dictDataApi.getDictDataList("diy-template-theme");
DictDataRespDTO dictDataRespDTO = dictDataList.get(0);
diyTemplateDO.setThemeType(dictDataRespDTO.getValue());
List<DictDataRespDTO> dictDataList1 = dictDataApi.getDictDataList("diy-template-goods");
DictDataRespDTO dictDataRespDTO1 = dictDataList1.get(0);
diyTemplateDO.setGoodsType(dictDataRespDTO1.getValue());
return diyTemplateDO;
} }
} }

View File

@ -31,6 +31,8 @@ public interface KeFuMessageService {
* @return 编号 * @return 编号
*/ */
Long sendKefuMessage(AppKeFuMessageSendReqVO sendReqVO); Long sendKefuMessage(AppKeFuMessageSendReqVO sendReqVO);
String sendKefuMessageTest(String s); String sendKefuMessageTest(String s);
/** /**

View File

@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi; import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi; import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessagePageReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessagePageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessagePageReqVO; import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessagePageReqVO;
@ -16,7 +17,10 @@ import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuM
import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO; import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO; import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.kefu.KeFuMessageMapper; import cn.iocoder.yudao.module.promotion.dal.mysql.kefu.KeFuMessageMapper;
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -51,9 +55,12 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
@Resource @Resource
private WebSocketSenderApi webSocketSenderApi; private WebSocketSenderApi webSocketSenderApi;
@Resource
private NotifyMessageMapper notifyMessageMapper;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Long sendKefuMessage(KeFuMessageSendReqVO sendReqVO) { public Long sendKefuMessage(KeFuMessageSendReqVO sendReqVO) { //客服发消息
// 1.1 校验会话是否存在 // 1.1 校验会话是否存在
KeFuConversationDO conversation = conversationService.validateKefuConversationExists(sendReqVO.getConversationId()); KeFuConversationDO conversation = conversationService.validateKefuConversationExists(sendReqVO.getConversationId());
// 1.2 校验接收人是否存在 // 1.2 校验接收人是否存在
@ -74,20 +81,39 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
} }
@Override @Override
public Long sendKefuMessage(AppKeFuMessageSendReqVO sendReqVO) { public Long sendKefuMessage(AppKeFuMessageSendReqVO sendReqVO) { //用户发消息
// 1.1 设置会话编号 // 1.1 设置会话编号
KeFuMessageDO kefuMessage = BeanUtils.toBean(sendReqVO, KeFuMessageDO.class); KeFuMessageDO kefuMessage = BeanUtils.toBean(sendReqVO, KeFuMessageDO.class);
KeFuConversationDO conversation = conversationService.getOrCreateConversation(sendReqVO.getSenderId()); KeFuConversationDO conversation = conversationService.getOrCreateConversation(sendReqVO.getSenderId());
kefuMessage.setConversationId(conversation.getId()); kefuMessage.setConversationId(conversation.getId());
// 1.2 保存消息 // 1.2 保存消息
kefuMessage.setReceiverId(conversation.getKefuId()).setReceiverType(UserTypeEnum.ADMIN.getValue()); // 设置接收人
keFuMessageMapper.insert(kefuMessage); keFuMessageMapper.insert(kefuMessage);
// 2. 更新会话消息冗余 // 2. 更新会话消息冗余
conversationService.updateConversationLastMessage(kefuMessage); conversationService.updateConversationLastMessage(kefuMessage);
getSelf().sendAsyncMessageToMembers(conversation.getKefuId(), KEFU_MESSAGE_TYPE, kefuMessage);
// 3. 通知所有管理员更新对话 // 3. 通知所有管理员更新对话
getSelf().sendAsyncMessageToAdmin(KEFU_MESSAGE_TYPE, kefuMessage); getSelf().sendAsyncMessageToAdmins(KEFU_MESSAGE_TYPE, kefuMessage);
return kefuMessage.getId(); return kefuMessage.getId();
} }
//添加站内信
// MemberUserRespDTO user = memberUserApi.getUser(sendReqVO.getSenderId());
// NotifyMessageDO notifyMessageDO = new NotifyMessageDO()
// .setUserId(sendReqVO.getSenderId())
// .setUserType(sendReqVO.getSenderType())
// .setTemplateId((long)1)
// .setTemplateCode()
// .setTemplateNickname(user.getNickname())
// .setTemplateContent("客户发来消息")
// .setTemplateType(2)
// .setTemplateParams()
// .setReadStatus(2);
// notifyMessageMapper.insert(notifyMessageDO);
@Override @Override
public String sendKefuMessageTest(String s){ public String sendKefuMessageTest(String s){
webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), 1L, "1", s);; webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), 1L, "1", s);;
@ -137,11 +163,21 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
webSocketSenderApi.sendObject(UserTypeEnum.MEMBER.getValue(), userId, messageType, content); webSocketSenderApi.sendObject(UserTypeEnum.MEMBER.getValue(), userId, messageType, content);
} }
@Async
public void sendAsyncMessageToMembers(Long userId, String messageType, Object content) {
webSocketSenderApi.sendObject(UserTypeEnum.MEMBER.getValue(), userId, messageType, content);
}
@Async @Async
public void sendAsyncMessageToAdmin(String messageType, Object content) { public void sendAsyncMessageToAdmin(String messageType, Object content) {
webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), messageType, content); webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), messageType, content);
} }
@Async
public void sendAsyncMessageToAdmins(String messageType, Object content) {
webSocketSenderApi.sendObject(UserTypeEnum.MEMBER.getValue(), messageType, content);
}
@Override @Override
public PageResult<KeFuMessageDO> getKeFuMessagePage(KeFuMessagePageReqVO pageReqVO) { public PageResult<KeFuMessageDO> getKeFuMessagePage(KeFuMessagePageReqVO pageReqVO) {
return keFuMessageMapper.selectPage(pageReqVO); return keFuMessageMapper.selectPage(pageReqVO);

View File

@ -27,4 +27,7 @@ public interface NotifyMessageSendApi {
*/ */
Long sendSingleMessageToMember(@Valid NotifySendSingleToUserReqDTO reqDTO); Long sendSingleMessageToMember(@Valid NotifySendSingleToUserReqDTO reqDTO);
} }

View File

@ -40,10 +40,12 @@ public class DictDataController {
@Resource @Resource
private DictDataService dictDataService; private DictDataService dictDataService;
@Resource @Resource
public DictDataApi dictDataApi; public DictDataApi dictDataApi;
@PostMapping("/create") @PostMapping("/create")
@Operation(summary = "新增字典数据") @Operation(summary = "新增字典数据")
@PreAuthorize("@ss.hasPermission('system:dict:create')") @PreAuthorize("@ss.hasPermission('system:dict:create')")
@ -159,6 +161,54 @@ public class DictDataController {
} }
/**
* 修改装修商品分类字典数据
*/
@GetMapping(value = "/diy-template-goods")
public CommonResult<String> setGoods(String id) {
List<DictDataRespDTO> dictDataList = dictDataApi.getDictDataList("diy-template-goods");
DictDataRespDTO dictDataRespDTO = dictDataList.get(0);
DictDataSaveReqVO dictDataSaveReqVO = BeanUtils.toBean(dictDataRespDTO, DictDataSaveReqVO.class);
dictDataSaveReqVO.setValue(id);
dictDataService.updateDictData(dictDataSaveReqVO);
return success(id);
}
/**
* 修改装修商品分类字典数据
*/
@GetMapping(value = "/getGoods")
public CommonResult<String> getGoods() {
List<DictDataRespDTO> dictDataList = dictDataApi.getDictDataList("diy-template-goods");
DictDataRespDTO dictDataRespDTO = dictDataList.get(0);
return success(dictDataRespDTO.getValue());
}
/**
* 修改装修装修风格字典数据
*/
@GetMapping(value = "/diy-template-theme")
public CommonResult<String> setTheme(String id) {
List<DictDataRespDTO> dictDataList = dictDataApi.getDictDataList("diy-template-theme");
DictDataRespDTO dictDataRespDTO = dictDataList.get(0);
DictDataSaveReqVO dictDataSaveReqVO = BeanUtils.toBean(dictDataRespDTO, DictDataSaveReqVO.class);
dictDataSaveReqVO.setValue(id);
dictDataService.updateDictData(dictDataSaveReqVO);
return success(id);
}
/**
* 修改装修装修风格字典数据
*/
@GetMapping(value = "/getTheme")
public CommonResult<String> getTheme() {
List<DictDataRespDTO> dictDataList = dictDataApi.getDictDataList("diy-template-theme");
DictDataRespDTO dictDataRespDTO = dictDataList.get(0);
return success(dictDataRespDTO.getValue());
}
} }

View File

@ -210,7 +210,7 @@ yudao:
websocket: websocket:
enable: true # websocket的开关 enable: true # websocket的开关
path: /infra/ws # 路径 path: /infra/ws # 路径
sender-type: local # 消息发送的类型,可选值为 local、redis、rocketmq、kafka、rabbitmq sender-type: redis # 消息发送的类型,可选值为 local、redis、rocketmq、kafka、rabbitmq
sender-rocketmq: sender-rocketmq:
topic: ${spring.application.name}-websocket # 消息发送的 RocketMQ Topic topic: ${spring.application.name}-websocket # 消息发送的 RocketMQ Topic
consumer-group: ${spring.application.name}-websocket-consumer # 消息发送的 RocketMQ Consumer Group consumer-group: ${spring.application.name}-websocket-consumer # 消息发送的 RocketMQ Consumer Group