企业微信发送消息
parent
00392d228d
commit
19cc177d9b
@ -0,0 +1,52 @@
|
||||
package com.baiye.core.base;
|
||||
|
||||
import com.baiye.core.base.api.Result;
|
||||
import com.baiye.core.base.api.ResultCode;
|
||||
import com.baiye.core.constant.MessageConstant;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date : 2022/8/11
|
||||
*/
|
||||
@Data
|
||||
@Getter
|
||||
@ApiModel(value = "websocket响应")
|
||||
public class WebSocketResponse<T> implements Serializable {
|
||||
|
||||
|
||||
@ApiModelProperty(value = "状态码", required = true)
|
||||
private int code;
|
||||
|
||||
@ApiModelProperty(value = "类型", required = true)
|
||||
private String cmd;
|
||||
|
||||
@ApiModelProperty(value = "业务数据")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private Object data;
|
||||
|
||||
public WebSocketResponse(int code, T data, String cmd) {
|
||||
this.code = code;
|
||||
this.cmd = cmd;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
|
||||
public static <T> WebSocketResponse<T> data(T data) {
|
||||
return data(data, MessageConstant.DEFAULT_SUCCESS_MESSAGE);
|
||||
}
|
||||
|
||||
public static <T> WebSocketResponse<T> data(T data, String msg) {
|
||||
return data(ResultCode.SUCCESS.getCode(), data, msg);
|
||||
}
|
||||
|
||||
public static <T> WebSocketResponse<T> data(int code, T data, String cmd) {
|
||||
return new WebSocketResponse<>(code, data, cmd);
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.baiye.core.base.api;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date : 2022/8/11
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum WebsocketCode {
|
||||
/**
|
||||
* 消息发送
|
||||
*/
|
||||
CHAT_SEND(1, "Chat.send"),
|
||||
|
||||
/**
|
||||
* 消息接收
|
||||
*/
|
||||
CHAT_MSG(2, "Chat.msg");
|
||||
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
final int code;
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
final String msg;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.baiye.core.constant;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date : 2022/8/9
|
||||
*/
|
||||
public class AdPlatFormConstants {
|
||||
private AdPlatFormConstants() {
|
||||
}
|
||||
|
||||
public static final String AD_PLATFORM = "ad-platform";
|
||||
|
||||
public static final String AD_PLATFORM_CALLBACK = "https://baiyee.vip/wechat/callback";
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.baiye.core.constant;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date : 2022/8/10
|
||||
*/
|
||||
public class EncryptionConstants {
|
||||
private EncryptionConstants(){
|
||||
}
|
||||
|
||||
public static final String ENCRYPTION_KEY = "93PmJkwaIOMYSJDB";
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.baiye.core.util;
|
||||
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.crypto.Mode;
|
||||
import cn.hutool.crypto.Padding;
|
||||
import cn.hutool.crypto.symmetric.AES;
|
||||
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date : 2022/8/10
|
||||
*/
|
||||
public class AesEncrypt {
|
||||
|
||||
private AesEncrypt() {
|
||||
//构造器
|
||||
}
|
||||
|
||||
/**
|
||||
* 对明文加密
|
||||
*
|
||||
* @param content 明文
|
||||
* @param key 密钥
|
||||
* @return
|
||||
*/
|
||||
public static String encrypt(String content, String key) {
|
||||
//加密为16进制表示
|
||||
return getAes(key).encryptHex(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对密文解密
|
||||
*
|
||||
* @param encryptContent 密文
|
||||
* @param key 密钥
|
||||
* @return
|
||||
*/
|
||||
public static String decrypt(String encryptContent, String key) {
|
||||
// 解密为字符串
|
||||
return getAes(key).decryptStr(encryptContent, CharsetUtil.CHARSET_UTF_8);
|
||||
}
|
||||
|
||||
private static AES getAes(String key) {
|
||||
//构建
|
||||
return new AES(Mode.ECB, Padding.PKCS5Padding, key.getBytes());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
server:
|
||||
port: 7070
|
||||
spring:
|
||||
application:
|
||||
name: auth-server
|
||||
profiles:
|
||||
active: dev
|
||||
ip:
|
||||
local-parsing: true
|
@ -1,30 +0,0 @@
|
||||
package com.baiye.config;
|
||||
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Component
|
||||
public class MyBatisHandler implements MetaObjectHandler {
|
||||
/**
|
||||
* 插入时的填充策略
|
||||
* @param metaObject
|
||||
*/
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
this.setFieldValByName("createTime",new Date(),metaObject);
|
||||
this.setFieldValByName("updateTime",new Date(),metaObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新时的填充策略
|
||||
* @param metaObject
|
||||
*/
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
this.setFieldValByName("updateTime",new Date(),metaObject);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
package com.baiye.module.controller;
|
||||
|
||||
import com.baiye.entity.MessageRecord;
|
||||
import com.baiye.listener.ChatMessageSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/demo01")
|
||||
public class Demo01Controller {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
|
||||
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package com.baiye.module.dao;
|
||||
|
||||
import com.baiye.entity.MessageRecord;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface MessageRecordMapper extends BaseMapper<MessageRecord> {
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<configuration debug="false" scan="false">
|
||||
<springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue=""/>
|
||||
<property name="log.path" value="logs/${spring.application.name}"/>
|
||||
<!-- 彩色日志格式 -->
|
||||
<property name="CONSOLE_LOG_PATTERN"
|
||||
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
|
||||
<!-- 彩色日志依赖的渲染类 -->
|
||||
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
|
||||
<conversionRule conversionWord="wex"
|
||||
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
|
||||
<conversionRule conversionWord="wEx"
|
||||
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
|
||||
<!-- Console log output -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- Log file debug output -->
|
||||
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/debug.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>50MB</maxFileSize>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- Log file error output -->
|
||||
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/error.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>50MB</maxFileSize>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>ERROR</level>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!--nacos 心跳 INFO 屏蔽-->
|
||||
<logger name="com.alibaba.nacos" level="OFF">
|
||||
<appender-ref ref="error"/>
|
||||
</logger>
|
||||
|
||||
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
|
||||
<root level="INFO">
|
||||
<appender-ref ref="console"/>
|
||||
<appender-ref ref="debug"/>
|
||||
<appender-ref ref="error"/>
|
||||
</root>
|
||||
</configuration>
|
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.baiye.module.dao.MessageRecordMapper">
|
||||
|
||||
</mapper>
|
@ -1,19 +0,0 @@
|
||||
package com.baiye;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date 2022-7-20
|
||||
*/
|
||||
|
||||
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
|
||||
public class CdpToolNettyDisruptorApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(CdpToolNettyDisruptorApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.baiye.service;
|
||||
|
||||
import io.netty.channel.group.ChannelGroup;
|
||||
import io.netty.channel.group.DefaultChannelGroup;
|
||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||
|
||||
public class ChannelGroupConfig {
|
||||
//存储每一个客户端接入进来的对象
|
||||
public static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package com.baiye.service;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date 2022-8-10
|
||||
* Netty 无法检测到客户端为飞行模式时, 自动关闭对应的通道资源
|
||||
* 自定义 HeartBeatHandler 定期对通道进行检测其是否空闲, 若空闲超过一定时间, 将通道资源关闭
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class HeartBeatHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
/**
|
||||
* 重写用户事件触发
|
||||
*/
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
|
||||
if (evt instanceof IdleStateEvent) {
|
||||
IdleStateEvent event = (IdleStateEvent) evt;
|
||||
switch (event.state()) {
|
||||
case READER_IDLE:
|
||||
log.info("读空闲触发。。。");
|
||||
break;
|
||||
case WRITER_IDLE:
|
||||
log.info("写空闲触发。。。");
|
||||
break;
|
||||
case ALL_IDLE:
|
||||
log.info("读写空闲触发。。。");
|
||||
ctx.channel().close();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.baiye.server;
|
||||
package com.baiye.service;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
@ -0,0 +1,50 @@
|
||||
package com.baiye.controller;
|
||||
|
||||
import com.baiye.core.base.api.Result;
|
||||
import com.baiye.core.page.PageResult;
|
||||
import com.baiye.query.WeChatPersonQuery;
|
||||
import com.baiye.service.PersonalWeChatUserService;
|
||||
import com.baiye.util.SecurityUtils;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date : 2022/8/8
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/wechat/addressBook")
|
||||
public class PersonalWeChatUserController {
|
||||
private final PersonalWeChatUserService personalWeChatUserService;
|
||||
|
||||
@ApiOperation("通讯录分页")
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult> query(WeChatPersonQuery weChatWordsQuery, Pageable pageable) {
|
||||
weChatWordsQuery.setUserId(SecurityUtils.getCurrentUserId());
|
||||
return Result.data(this.personalWeChatUserService.selectPage(weChatWordsQuery, pageable));
|
||||
}
|
||||
|
||||
@ApiOperation("修改星标")
|
||||
@GetMapping("/updateStar")
|
||||
public Result<Void> updateStar(Long userId, Boolean startFlag) {
|
||||
Long currentUserId = SecurityUtils.getCurrentUserId();
|
||||
this.personalWeChatUserService.changeUserStart(userId, currentUserId, startFlag);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@ApiOperation("星标分页")
|
||||
@GetMapping("/startPage")
|
||||
public Result<PageResult> startPageQuery(WeChatPersonQuery weChatWordsQuery, Pageable pageable) {
|
||||
weChatWordsQuery.setUserId(SecurityUtils.getCurrentUserId());
|
||||
weChatWordsQuery.setIsStart(Boolean.TRUE);
|
||||
return Result.data(this.personalWeChatUserService.selectPage(weChatWordsQuery, pageable));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
package com.baiye.job;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baiye.core.base.api.Result;
|
||||
import com.baiye.core.constant.DefaultNumberConstants;
|
||||
import com.baiye.core.constant.HttpStatus;
|
||||
import com.baiye.core.constant.WeChatRequestConstants;
|
||||
import com.baiye.dto.SyncWeChatAddressBookDTO;
|
||||
import com.baiye.dto.WeChatAddressBookDTO;
|
||||
import com.baiye.entity.EnterpriseWeChatUser;
|
||||
import com.baiye.entity.PersonalWeChatUser;
|
||||
import com.baiye.properties.WeChatProperties;
|
||||
import com.baiye.service.PersonalWeChatUserService;
|
||||
import com.baiye.service.WeChatUserService;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.MapDifference;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date : 2022/8/8
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SyncFriendsTask {
|
||||
|
||||
|
||||
private final WeChatProperties weChatProperties;
|
||||
|
||||
private final WeChatUserService weChatUserService;
|
||||
|
||||
private final PersonalWeChatUserService personalWeChatUserService;
|
||||
|
||||
|
||||
/**
|
||||
* 8分钟同步通讯录
|
||||
*/
|
||||
@Scheduled(cron = "0 0/8 * * * ?")
|
||||
public void syncContacts() {
|
||||
long startTime = DateUtil.date().getTime();
|
||||
log.info("=================== the sync account time as {} ===================", startTime);
|
||||
List<EnterpriseWeChatUser> byWeChatList =
|
||||
weChatUserService.findByWeChatList();
|
||||
if (CollUtil.isNotEmpty(byWeChatList)) {
|
||||
for (EnterpriseWeChatUser chatUser : byWeChatList) {
|
||||
Result<Object> objectResult =
|
||||
weChatUserService.syncContact(chatUser.getWxId());
|
||||
log.info("=================== the sync account result as {} =================== ",
|
||||
JSONUtil.toJsonStr(objectResult));
|
||||
}
|
||||
}
|
||||
long endTime = DateUtil.date().getTime();
|
||||
log.info("=================== the sync account consuming time as {} =================== ", endTime - startTime);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 10分钟同步好友信息
|
||||
*/
|
||||
@Scheduled(cron = "0 0/10 * * * ?")
|
||||
public void syncUser() {
|
||||
// 查询所有用户
|
||||
List<EnterpriseWeChatUser> byWeChatList =
|
||||
weChatUserService.findByWeChatList();
|
||||
if (CollUtil.isNotEmpty(byWeChatList)) {
|
||||
for (EnterpriseWeChatUser chatUser : byWeChatList) {
|
||||
// 查询通讯录
|
||||
Result<Object> allContact = weChatUserService.getAllContact(chatUser.getWxId());
|
||||
if (allContact.getCode() == HttpStatus.SUCCESS) {
|
||||
List<WeChatAddressBookDTO> chatAddressBookDTOList =
|
||||
Convert.toList(WeChatAddressBookDTO.class, allContact.getData());
|
||||
if (CollUtil.isNotEmpty(chatAddressBookDTOList)) {
|
||||
// 查询已插入用户
|
||||
List<String> strings =
|
||||
personalWeChatUserService.
|
||||
selectUserIdListByCurrentUserId(chatUser.getWxId());
|
||||
// list转集合
|
||||
Map<String, WeChatAddressBookDTO> collect =
|
||||
chatAddressBookDTOList.stream().collect
|
||||
(Collectors.toMap(WeChatAddressBookDTO::getUserName,
|
||||
account -> account));
|
||||
// set差集比较
|
||||
Set<String> querySet = collect.keySet();
|
||||
Set<String> newHashSet = Sets.newHashSet(strings);
|
||||
Sets.SetView<String> difference =
|
||||
Sets.difference(querySet, newHashSet);
|
||||
log.info("======================= the difference size as {} =======================",difference.size());
|
||||
for (String weChatUserId : difference) {
|
||||
WeChatAddressBookDTO dto = collect.get(weChatUserId);
|
||||
// 时间判断
|
||||
if (dto.getAddTime() != null &&
|
||||
dto.getAddTime() > DefaultNumberConstants.ZERO_NUMBER) {
|
||||
PersonalWeChatUser weChatUser = new PersonalWeChatUser();
|
||||
weChatUser.setSex(dto.getSex());
|
||||
weChatUser.setIsStar(Boolean.FALSE);
|
||||
weChatUser.setNickname(dto.getNickName());
|
||||
weChatUser.setWeChatId(dto.getUserName());
|
||||
weChatUser.setUsername(dto.getUserName());
|
||||
weChatUser.setUserId(chatUser.getOwnerId());
|
||||
weChatUser.setEnterpriseId(chatUser.getWxId());
|
||||
weChatUser.setAddTime(DateUtil.date(dto.getAddTime()));
|
||||
weChatUser.setBigHeadImgUrl(dto.getBigHeadImgUrl());
|
||||
// 插入数据
|
||||
personalWeChatUserService.save(weChatUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
package com.baiye.listener;
|
||||
|
||||
import org.springframework.cloud.stream.annotation.Input;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
|
||||
public interface MySink {
|
||||
|
||||
|
||||
String ERBADAGANG_INPUT = "wechat-input";
|
||||
|
||||
@Input(ERBADAGANG_INPUT)
|
||||
SubscribableChannel demo01Input();
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package com.baiye.listener;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baiye.core.constant.CacheKey;
|
||||
import com.baiye.core.constant.DefaultNumberConstants;
|
||||
import com.baiye.core.util.RedisUtils;
|
||||
import com.baiye.dto.TransmissionMessageDTO;
|
||||
import com.baiye.entity.EnterpriseWeChatUser;
|
||||
import com.baiye.service.PersonalWeChatUserService;
|
||||
import com.baiye.service.SocketServerHandler;
|
||||
import com.baiye.service.WeChatUserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.stream.annotation.StreamListener;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date 2022-8-9
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class WeChatMessageConsumer {
|
||||
|
||||
private final RedisUtils redisUtils;
|
||||
|
||||
private final WeChatUserService weChatUserService;
|
||||
|
||||
private final SocketServerHandler socketServerHandler;
|
||||
|
||||
@StreamListener(MySink.ERBADAGANG_INPUT)
|
||||
public void onMessage(Message<?> message) {
|
||||
TransmissionMessageDTO messageDTO = JSONUtil.toBean
|
||||
(JSONUtil.toJsonStr(message.getPayload()), TransmissionMessageDTO.class);
|
||||
if (ObjectUtil.isNotNull(messageDTO)) {
|
||||
Long recordId = messageDTO.getId();
|
||||
String wechatId = messageDTO.getWechatId();
|
||||
// 防止重复消费
|
||||
Object messageRecordCache =
|
||||
redisUtils.get(CacheKey.MESSAGE_KEY + recordId);
|
||||
if (ObjectUtil.isNull(messageRecordCache)) {
|
||||
EnterpriseWeChatUser byWechat
|
||||
= weChatUserService.findByWechat(wechatId);
|
||||
messageDTO.setEnterpriseWeChatNickname(byWechat.getNickname());
|
||||
messageDTO.setEnterpriseWeChatAvatar(byWechat.getSmallHeadImgUrl());
|
||||
socketServerHandler.sendMessage
|
||||
(wechatId, BeanUtil.beanToMap(messageDTO));
|
||||
redisUtils.set(CacheKey.MESSAGE_KEY + recordId,
|
||||
recordId, DefaultNumberConstants.NINETY, TimeUnit.MINUTES);
|
||||
}
|
||||
}
|
||||
log.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message);
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
#配置数据源
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: ${NACOS_HOST:172.16.69.134}:${NACOS_PORT:8848}
|
||||
# Spring Cloud Stream 配置项,对应 BindingServiceProperties 类
|
||||
stream:
|
||||
# Binding 配置项,对应 BindingProperties Map
|
||||
bindings:
|
||||
wechat-input:
|
||||
destination: WECHAT-TOPIC # 目的地。这里使用 RocketMQ Topic
|
||||
content-type: application/json # 内容格式。这里使用 JSON
|
||||
group: wechat-consumer-group-wechat-topic-01 # 消费者分组,命名规则:组名+topic名
|
||||
consumer:
|
||||
max-attempts: 2
|
||||
# Spring Cloud Stream RocketMQ 配置项
|
||||
rocketmq:
|
||||
# RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类
|
||||
binder:
|
||||
name-server: 39.103.195.38:9876 # RocketMQ Namesrv 地址
|
||||
# RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map
|
||||
bindings:
|
||||
wechat-input:
|
||||
# RocketMQ Consumer 配置项,对应 RocketMQConsumerProperties 类
|
||||
consumer:
|
||||
enabled: true # 是否开启消费,默认为 true
|
||||
broadcasting: false # 是否使用广播消费,默认为 false 使用集群消费
|
||||
orderly: true # 是否顺序消费,默认为 false 并发消费。
|
||||
redis:
|
||||
database: 3
|
||||
host: 8.130.96.163
|
||||
timeout: 5000
|
||||
datasource:
|
||||
druid:
|
||||
db-type: com.alibaba.druid.pool.DruidDataSource
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
url: jdbc:mysql://rm-bp1693kl5d490o5cn.mysql.rds.aliyuncs.com:3306/db_wechat?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull
|
||||
username: root
|
||||
password: yuyou@RDS2020
|
||||
# 初始连接数
|
||||
initial-size: 5
|
||||
# 最小连接数
|
||||
min-idle: 15
|
||||
# 最大连接数
|
||||
max-active: 30
|
||||
# 超时时间(以秒数为单位)
|
||||
remove-abandoned-timeout: 180
|
||||
# 获取连接超时时间
|
||||
max-wait: 3000
|
||||
# 连接有效性检测时间
|
||||
time-between-eviction-runs-millis: 60000
|
||||
# 连接在池中最小生存的时间
|
||||
min-evictable-idle-time-millis: 300000
|
||||
# 连接在池中最大生存的时间
|
||||
max-evictable-idle-time-millis: 900000
|
||||
# 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除
|
||||
test-while-idle: true
|
||||
# 指明是否在从池中取出连接前进行检验,如果检验失败, 则从池中去除连接并尝试取出另一个
|
||||
test-on-borrow: true
|
||||
# 是否在归还到池中前进行检验
|
||||
test-on-return: false
|
||||
# 检测连接是否有效
|
||||
validation-query: select 1
|
||||
# 配置监控统计
|
||||
webStatFilter:
|
||||
enabled: true
|
||||
stat-view-servlet:
|
||||
enabled: true
|
||||
url-pattern: /druid/*
|
||||
reset-enable: false
|
||||
filter:
|
||||
stat:
|
||||
enabled: true
|
||||
# 记录慢SQL
|
||||
log-slow-sql: true
|
||||
slow-sql-millis: 1000
|
||||
merge-sql: false
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
|
||||
#是否开启 swagger-ui
|
||||
swagger:
|
||||
enabled: true
|
||||
|
||||
# IP 本地解析
|
||||
ip:
|
||||
local-parsing: true
|
||||
|
@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<configuration debug="false" scan="false">
|
||||
<springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue=""/>
|
||||
<property name="log.path" value="logs/${spring.application.name}"/>
|
||||
<!-- 彩色日志格式 -->
|
||||
<property name="CONSOLE_LOG_PATTERN"
|
||||
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
|
||||
<!-- 彩色日志依赖的渲染类 -->
|
||||
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
|
||||
<conversionRule conversionWord="wex"
|
||||
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
|
||||
<conversionRule conversionWord="wEx"
|
||||
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
|
||||
<!-- Console log output -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- Log file debug output -->
|
||||
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/debug.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>50MB</maxFileSize>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- Log file error output -->
|
||||
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/error.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>50MB</maxFileSize>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>ERROR</level>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!--nacos 心跳 INFO 屏蔽-->
|
||||
<logger name="com.alibaba.nacos" level="OFF">
|
||||
<appender-ref ref="error"/>
|
||||
</logger>
|
||||
|
||||
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
|
||||
<root level="INFO">
|
||||
<appender-ref ref="console"/>
|
||||
<appender-ref ref="debug"/>
|
||||
<appender-ref ref="error"/>
|
||||
</root>
|
||||
</configuration>
|
@ -0,0 +1,25 @@
|
||||
package com.baiye.convert;
|
||||
|
||||
import cn.hutool.extra.emoji.EmojiUtil;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date : 2021/12/28
|
||||
*/
|
||||
@Converter
|
||||
public class EmojiConverterListJson implements AttributeConverter<Object, String> {
|
||||
|
||||
|
||||
@Override
|
||||
public String convertToDatabaseColumn(Object obj) {
|
||||
return EmojiUtil.toAlias(obj.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convertToEntityAttribute(String s) {
|
||||
return EmojiUtil.toUnicode(s);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.baiye.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date : 2022/8/8
|
||||
*/
|
||||
@Data
|
||||
public class SyncWeChatAddressBookDTO {
|
||||
|
||||
private List<WeChatAddressBookDTO> data;
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.baiye.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date : 2022/8/8
|
||||
*/
|
||||
@Data
|
||||
public class WeChatAddressBookDTO {
|
||||
|
||||
private Integer sex;
|
||||
private Integer type;
|
||||
private Long addTime;
|
||||
private Integer msgType;
|
||||
private String userName;
|
||||
private String nickName;
|
||||
private String companyId;
|
||||
private Integer verifyFlag;
|
||||
private Integer contactType;
|
||||
private Integer maybeSource1;
|
||||
private Integer maybeSource2;
|
||||
private String bigHeadImgUrl;
|
||||
private Integer chatroomVersion;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.baiye.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date : 2022/8/12
|
||||
*/
|
||||
@Data
|
||||
public class WebsocketMessageRequestDTO {
|
||||
|
||||
@ApiModelProperty(value = "用户微信id")
|
||||
private String userWechatId;
|
||||
|
||||
@ApiModelProperty(value = "微信用户id")
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty(value = "企业微信")
|
||||
private String enterpriseId;
|
||||
|
||||
@ApiModelProperty(value = "消息主要内容")
|
||||
private String content;
|
||||
|
||||
@ApiModelProperty(value = "消息类型")
|
||||
private Integer messageType;
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package com.baiye.query;
|
||||
|
||||
import com.baiye.annotation.Query;
|
||||
import com.baiye.annotation.type.SelectType;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author
|
||||
* @enzo
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class WeChatPersonQuery {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Query
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 昵称查询
|
||||
*/
|
||||
@Query(type = SelectType.INNER_LIKE)
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 时间段查询
|
||||
*/
|
||||
@Query(type = SelectType.BETWEEN)
|
||||
private List<Timestamp> createTime;
|
||||
|
||||
/**
|
||||
* 星标查询
|
||||
*/
|
||||
@Query
|
||||
private Boolean isStart;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.baiye.properties;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date : 2022/8/10
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "aliyun.oss.file")
|
||||
public class AliYunOssProperties {
|
||||
|
||||
|
||||
private String bucketName;
|
||||
|
||||
private String endpoint;
|
||||
|
||||
private String accessKeyId;
|
||||
|
||||
private String accessKeySecret;
|
||||
|
||||
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 332 KiB After Width: | Height: | Size: 0 B |
Binary file not shown.
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 0 B |
Binary file not shown.
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 0 B |
Loading…
Reference in New Issue