浏览代码

消息推送单独服务

黄远 5 年之前
父节点
当前提交
9617560bab

+ 53 - 0
lift-push-pc/pom.xml

@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>cn.com.ty</groupId>
+        <artifactId>lift-server</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>lift-push-pc</artifactId>
+
+    <!--添加依赖 -->
+    <dependencies>
+        <!-- 添加websocket依赖 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+
+        <!-- 添加公共模块依赖 -->
+        <dependency>
+            <groupId>cn.com.ty</groupId>
+            <artifactId>lift-common</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.xwy</groupId>
+            <artifactId>xwy-spring-boot</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>cn.com.ty.lift.push.PushPCApplication</mainClass>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 27 - 0
lift-push-pc/src/main/java/cn/com/ty/lift/push/PushPCApplication.java

@@ -0,0 +1,27 @@
+package cn.com.ty.lift.push;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
+import org.springframework.jms.annotation.EnableJms;
+import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
+
+/**
+ * 消息推送
+ *
+ * @author wcz
+ * @since 2020/4/13
+ */
+@EnableJms
+@EnableFeignClients
+@MapperScan("cn.com.ty.**.mapper")
+@SpringBootApplication
+@EnableRedisHttpSession
+@EnableRedisRepositories
+public class PushPCApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(PushPCApplication.class, args);
+    }
+}

+ 35 - 0
lift-push-pc/src/main/java/cn/com/ty/lift/push/app/PushConsumer.java

@@ -0,0 +1,35 @@
+package cn.com.ty.lift.push.app;
+
+import cn.com.ty.lift.common.constants.WebSocketConstants;
+import cn.com.ty.lift.common.model.TimeMessage;
+import cn.com.ty.lift.push.message.service.TimeMessageService;
+import cn.com.ty.lift.push.websocket.handler.TimeyMessageHandler;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.jms.annotation.JmsListener;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 消息推送消费者,监听队列,从队列中取出message执行推送, queue >>> device platform
+ *
+ * @author wcz
+ * @since 2020/4/26
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class PushConsumer {
+
+    private TimeMessageService TimeMessageService;
+    private RedisTemplate redisTemplate;
+
+    @JmsListener(destination = WebSocketConstants.PUSH_MESSAGE_TO_PC)
+    public void listenPCAllQueue(List<TimeMessage> timeMessageList) {
+        log.info("listen {} receive message: {}", WebSocketConstants.PUSH_MESSAGE_TO_PC, timeMessageList);
+        TimeyMessageHandler timeMessageHandler = new TimeyMessageHandler(TimeMessageService, redisTemplate);
+        timeMessageHandler.saveAndPushMessageToUsers(timeMessageList);
+    }
+}

+ 96 - 0
lift-push-pc/src/main/java/cn/com/ty/lift/push/conf/SystemConfiguration.java

@@ -0,0 +1,96 @@
+package cn.com.ty.lift.push.conf;
+
+import cn.com.ty.lift.common.sql.SqlAnalysisInterceptor;
+import cn.com.ty.lift.common.sql.SqlIllegalInterceptor;
+import cn.com.ty.lift.common.utils.ValuePool;
+import cn.hutool.core.date.DatePattern;
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.activemq.command.ActiveMQQueue;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.jms.Queue;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * <p>
+ *     配置bean注入
+ * </p>
+ * @author wcz
+ * @since  2019/12/3
+ */
+@Slf4j
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "xinge")
+public class SystemConfiguration {
+
+    /**
+     * 全局格式化日期
+     */
+    @Bean
+    public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
+        return builder -> {
+            builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
+            builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN)));
+            builder.serializers(new LocalTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN)));
+
+            builder.deserializers(new LocalDateDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN)));
+            builder.deserializers(new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
+            builder.deserializers(new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN)));
+
+            builder.serializerByType(Long.class, ToStringSerializer.instance);
+        };
+    }
+
+    /**
+     * mybatis-plus 执行sql性能监测
+     */
+    @Bean
+    @ConditionalOnMissingBean(PerformanceInterceptor.class)
+    public SqlAnalysisInterceptor sqlAnalysisInterceptor(){
+        SqlAnalysisInterceptor sqlAnalysisInterceptor = new SqlAnalysisInterceptor();
+        //格式化执行的sql
+        sqlAnalysisInterceptor.setFormat(true);
+        //sql写入日志文件
+        sqlAnalysisInterceptor.setLogWrite(true);
+        return sqlAnalysisInterceptor;
+    }
+
+    /**
+     * SQL是影响系统性能最重要的因素,所以拦截提示不合理的SQL语句
+     */
+//    @Bean
+//    @ConditionalOnMissingBean
+    public SqlIllegalInterceptor sqlIllegalInterceptor(){
+        SqlIllegalInterceptor sqlIllegalInterceptor = new SqlIllegalInterceptor();
+        return sqlIllegalInterceptor;
+    }
+
+    /**
+     * 分页插件
+     * @return 分页插件
+     */
+    @Bean
+    public PaginationInterceptor paginationInterceptor() {
+        return new PaginationInterceptor();
+    }
+
+    @Bean
+    public Queue pcMessageQueue() {
+        return new ActiveMQQueue(ValuePool.PUSH_QUEUE_IOS_ALL);
+    }
+}

+ 7 - 0
lift-push-pc/src/main/java/cn/com/ty/lift/push/message/dao/mapper/TimeMessageMapper.java

@@ -0,0 +1,7 @@
+package cn.com.ty.lift.push.message.dao.mapper;
+
+import cn.com.ty.lift.common.model.TimeMessage;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+public interface TimeMessageMapper extends BaseMapper<TimeMessage> {
+}

+ 10 - 0
lift-push-pc/src/main/java/cn/com/ty/lift/push/message/service/TimeMessageService.java

@@ -0,0 +1,10 @@
+package cn.com.ty.lift.push.message.service;
+
+import cn.com.ty.lift.common.model.TimeMessage;
+import cn.com.ty.lift.push.message.dao.mapper.TimeMessageMapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+@Service
+public class TimeMessageService extends ServiceImpl<TimeMessageMapper, TimeMessage> {
+}

+ 44 - 0
lift-push-pc/src/main/java/cn/com/ty/lift/push/websocket/config/WebSocketConfig.java

@@ -0,0 +1,44 @@
+package cn.com.ty.lift.push.websocket.config;
+
+import cn.com.ty.lift.push.message.service.TimeMessageService;
+import cn.com.ty.lift.push.websocket.handler.TimeyMessageHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
+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;
+import java.util.concurrent.Executors;
+
+/**
+ * @ClassName WebSocketConfig
+ * @Description 消息推动配置
+ * @Author huangyuan
+ * @Date 2019-04-19 22:53
+ **/
+@Configuration
+@EnableWebSocket
+public class WebSocketConfig implements WebSocketConfigurer {
+
+    @Resource
+    private TimeMessageService  timeMessageService;
+
+    @Resource
+    private RedisTemplate redisTemplate;
+
+    @Bean
+    public TaskScheduler taskScheduler() {
+        return new ConcurrentTaskScheduler(Executors.newSingleThreadScheduledExecutor());
+    }
+
+    @Override
+    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
+        registry.addHandler(new TimeyMessageHandler(timeMessageService, redisTemplate), "/myMessageHandler")
+                .setAllowedOrigins("*")
+                .addInterceptors(new WebSocketInterceptor());
+    }
+}

+ 38 - 0
lift-push-pc/src/main/java/cn/com/ty/lift/push/websocket/config/WebSocketInterceptor.java

@@ -0,0 +1,38 @@
+package cn.com.ty.lift.push.websocket.config;
+
+import cn.com.ty.lift.common.constants.ApiConstants;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.http.server.ServletServerHttpRequest;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.server.HandshakeInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+
+public class WebSocketInterceptor implements HandshakeInterceptor {
+
+    /**
+     * 进入handler之前的拦截
+     */
+    @Override
+    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
+                                   WebSocketHandler wsHandler, Map<String, Object> map) {
+
+        if (request instanceof ServletServerHttpRequest) {
+            ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;
+            HttpServletRequest httpServletRequest = serverHttpRequest.getServletRequest();
+            //获取token
+            String authorizationToken = httpServletRequest.getParameter(ApiConstants.AUTHORIZATION_TOKEN);
+            map.put(ApiConstants.AUTHORIZATION_TOKEN, authorizationToken);
+        }
+        return true;
+    }
+
+    @Override
+    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
+                               Exception exception) {
+        System.out.println("进入成功");
+    }
+
+}

+ 185 - 0
lift-push-pc/src/main/java/cn/com/ty/lift/push/websocket/handler/TimeyMessageHandler.java

@@ -0,0 +1,185 @@
+package cn.com.ty.lift.push.websocket.handler;
+
+import cn.com.ty.lift.common.constants.ApiConstants;
+import cn.com.ty.lift.common.constants.WebSocketConstants;
+import cn.com.ty.lift.common.model.TimeMessage;
+import cn.com.ty.lift.push.message.service.TimeMessageService;
+import cn.com.ty.lift.push.websocket.model.RealTimeMessage;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.web.socket.*;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class TimeyMessageHandler implements WebSocketHandler {
+
+    private TimeMessageService timeMessageService;
+
+    private RedisTemplate redisTemplate;
+
+    public TimeyMessageHandler() {
+    }
+
+    public TimeyMessageHandler(TimeMessageService TimeMessageService, RedisTemplate redisTemplate) {
+        this.timeMessageService = TimeMessageService;
+        this.redisTemplate = redisTemplate;
+    }
+
+    //在线用户列表
+    private static final Map<Long, WebSocketSession> users;
+
+    static {
+        users = new HashMap<>();
+    }
+
+    //新增socket
+    @Override
+    public void afterConnectionEstablished(WebSocketSession session) {
+        //用户登录连接后,将用户id放入到在线用户列表中
+        Long userId = getCurrentUser(session);
+        if (userId == null) {
+            users.put(userId, session);
+            //获取用户实时消息
+            List<TimeMessage> TimeMessageList = timeMessageService.list(new QueryWrapper<TimeMessage>()
+                    .eq("view_flag", WebSocketConstants.IS_NOT_VIEW)
+                    .eq("user_id", userId)
+                    .eq("device_model", ApiConstants.DeviceModel.PC)
+            );
+            //将消息推送给用户
+            pushMessageToUser(userId, TimeMessageList);
+        }
+    }
+
+    //接收socket信息
+    @Override
+    public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) {
+
+    }
+
+    /**
+     * @param TimeMessages 推送的消息
+     * @return 是否推送成功
+     * @description 保存用户信息,并将消息推送给多个用户
+     * @date 2020/5/2 4:28 下午
+     */
+    public boolean saveAndPushMessageToUsers(List<TimeMessage> TimeMessages) {
+        if (TimeMessages != null && TimeMessages.size() > 0) {
+            //先将即使消息保存到数据库
+            if (timeMessageService.saveBatch(TimeMessages)) {
+                List<Long> userIds = new ArrayList<>();
+                TimeMessages.forEach(TimeMessage -> {
+                    userIds.add(TimeMessage.getUserId());
+                });
+                return pushMessageToUsers(userIds, TimeMessages);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return boolean
+     * @Author huangyuan
+     * @Description 给多个用户推送消息
+     * @Date 21:36 2019-04-19
+     * @Param [userIds, TimeMessages, count]
+     **/
+    public static boolean pushMessageToUsers(List<Long> userIds, List<TimeMessage> TimeMessages) {
+        try {
+            if (userIds != null && userIds.size() > 0) {
+                for (Long userId : userIds) {
+                    WebSocketSession session = users.get(userId);
+                    if (session != null) {
+                        //新建消息体
+                        RealTimeMessage realTimeMessage;
+                        if (TimeMessages != null && TimeMessages.size() > 0) {
+                            realTimeMessage = new RealTimeMessage(TimeMessages);
+                            realTimeMessage.setSize((long) TimeMessages.size());
+                        } else {
+                            realTimeMessage = new RealTimeMessage();
+                            realTimeMessage.setSize(0L);
+                        }
+                        session.sendMessage(new TextMessage(JSONUtil.parseObj(realTimeMessage).toString()));
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+        return true;
+    }
+
+
+    /**
+     * @return boolean
+     * @Author huangyuan
+     * @Description 把消息推给单个用户
+     * @Date 21:35 2019-04-19
+     * @Param [userId, TimeMessages]
+     **/
+    public static boolean pushMessageToUser(Long userId, List<TimeMessage> TimeMessages) {
+        List<Long> userIds = new ArrayList<>();
+        userIds.add(userId);
+        return pushMessageToUsers(userIds, TimeMessages);
+    }
+
+    /**
+     * @return boolean
+     * @Author huangyuan
+     * @Description 给单独用户推送信息
+     * @Date 00:35 2019-04-20
+     * @Param [userId, TimeMessage]
+     **/
+    public static boolean pushMessageToUser(Long userId, TimeMessage TimeMessage) {
+        List<TimeMessage> TimeMessageList = new ArrayList<>();
+        TimeMessageList.add(TimeMessage);
+        return pushMessageToUser(userId, TimeMessageList);
+    }
+
+    @Override
+    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
+        if (session.isOpen()) {
+            session.close();
+        }
+        System.out.println("连接出错");
+        users.remove(getCurrentUser(session));
+    }
+
+    @Override
+    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
+        System.out.println("连接已关闭:" + status);
+        users.remove(getCurrentUser(session));
+    }
+
+    @Override
+    public boolean supportsPartialMessages() {
+        return false;
+    }
+
+    /**
+     * 获取用户id,作为标识
+     *
+     * @param session
+     * @return Long
+     * @throws
+     * @author huangy
+     * @date 2018年9月18日
+     */
+    private Long getCurrentUser(WebSocketSession session) {
+        String authToken = (String) session.getAttributes().get(ApiConstants.AUTHORIZATION_TOKEN);
+        Object mobileUserInfo = redisTemplate.opsForValue().get(authToken);
+        if (mobileUserInfo != null) {
+            Object userInfo = redisTemplate.opsForValue().get(mobileUserInfo);
+            if (userInfo != null) {
+                Map<String, Object> currentUserInfoMap = JSONUtil.parseObj(userInfo);
+                return (Long) currentUserInfoMap.get(ApiConstants.CURRENT_USER_ID);
+            }
+        }
+        return null;
+    }
+
+}

+ 26 - 0
lift-push-pc/src/main/java/cn/com/ty/lift/push/websocket/model/RealTimeMessage.java

@@ -0,0 +1,26 @@
+package cn.com.ty.lift.push.websocket.model;
+
+import cn.com.ty.lift.common.model.TimeMessage;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 用于实时消息的推送
+ * @author huangy
+ * @date 2018年9月19日
+ */
+@Data
+public class RealTimeMessage {
+	
+	private Long size;//消息数量
+	private List<TimeMessage> msgList = new ArrayList<>();//及时消息列表
+	
+	public RealTimeMessage() {}
+	
+	public RealTimeMessage(List<TimeMessage> msgList) {
+		this.msgList = msgList;
+	}
+	
+}

+ 50 - 0
lift-push-pc/src/main/resources/application-prod.yml

@@ -0,0 +1,50 @@
+spring:
+  datasource:
+    url: jdbc:mysql://rm-bp1qkwy173koi066x.mysql.rds.aliyuncs.com:3306/rdsliftmanager?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
+    username: tykj
+    password: Tdkg-13250409
+    driver-class-name: com.mysql.cj.jdbc.Driver
+
+  #redis缓存配置
+  redis:
+    database: 0 #数据库索引,默认为0
+    host: r-bp1j7odydtfqtprdvm.redis.rds.aliyuncs.com #(内网地址)服务器地址
+    port: 6379 #端口
+    password: Gdkc-290431 #验证密码
+    jedis:
+      pool:
+        max-active: 8 #最大连接数
+        max-idle: 8 #最大空闲链接
+        max-wait: 20000ms
+        min-idle: 0
+    timeout: 600000s
+  activemq:
+    #ActiveMQ通讯地址
+    broker-url: tcp://172.16.24.145:61616
+    #用户名
+    user: admin
+    #密码
+    password: admin
+    #true 表示使用内置的MQ,false则连接服务器: 消息持久化就必须spring.activemq.in-memory=false选项
+    in-memory: false
+    packages:
+      #信任所有的包: 如果直接发送对象消息,那么必须设置spring.activemq.packages.trust-all为true
+      trust-all: true
+    pool:
+      #true表示使用连接池;false时,每发送一条数据创建一个连接
+      enabled: true
+      #连接池最大连接数
+      max-connections: 30
+      #空闲的连接过期时间,默认为30秒
+      idle-timeout: 3000s
+  jms:
+    #如果是点对点(queue),那么此处默认应该是false,如果发布订阅,那么一定设置为true
+    pub-sub-domain: false
+
+#aliyun OSS服务配置信息,加载到SystemConfiguration
+aliyun:
+  oss:
+    endpoint: http://oss-cn-hangzhou.aliyuncs.com
+    bucketName: ty-oss-file
+    accessKeyId: LTAI4G1iLTczu8JxM2En57gY
+    accessKeySecret: VHeRj3C2br87Uh447ruHsSTvYSDWeC

+ 47 - 0
lift-push-pc/src/main/resources/application-test.yml

@@ -0,0 +1,47 @@
+spring:
+  datasource:
+    url: jdbc:mysql://111.47.6.224:13769/rdsliftmanager?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
+    username: root
+    password: Tyty-2020
+    driver-class-name: com.mysql.cj.jdbc.Driver
+
+  #redis缓存配置
+  redis:
+    database: 0 #数据库索引,默认为0
+    host: 111.47.6.224 #服务器地址
+    port: 13789 #端口
+    password: newtyty #验证密码
+    jedis:
+      pool:
+        max-active: 8 #最大连接数
+        max-idle: 8 #最大空闲链接
+        max-wait: 20000ms
+        min-idle: 0
+  activemq:
+    #ActiveMQ通讯地址
+    broker-url: tcp://121.40.209.47:61616
+    #用户名
+    user: admin
+    #密码
+    password: admin
+    #true 表示使用内置的MQ,false则连接服务器: 消息持久化就必须spring.activemq.in-memory=false选项
+    in-memory: false
+    packages:
+      #信任所有的包: 如果直接发送对象消息,那么必须设置spring.activemq.packages.trust-all为true
+      trust-all: true
+    pool:
+      #true表示使用连接池;false时,每发送一条数据创建一个连接
+      enabled: true
+      #连接池最大连接数
+      max-connections: 30
+      #空闲的连接过期时间,默认为30秒
+      idle-timeout: 3000s
+  jms:
+    #如果是点对点(queue),那么此处默认应该是false,如果发布订阅,那么一定设置为true
+    pub-sub-domain: false
+
+
+#xinge服务配置信息,加载到SystemConfiguration
+xinge:
+    appId: 40d4e2e854433
+    secretKey: fa99dc19157b00fc71cf8d7e840273a3

+ 17 - 0
lift-push-pc/src/main/resources/application.yml

@@ -0,0 +1,17 @@
+server:
+  port: 20240
+  tomcat:
+    basedir: /Users/huangyuan/application/tomcat/data/tomcat_temp
+
+spring:
+  application:
+    name: lift-push-service
+  profiles:
+    active: test
+  datasource:
+    hikari:
+      max-lifetime: 1200000 # default: 1800000
+
+mybatis-plus:
+  mapper-locations: classpath*:/mapper/**/**Mapper.xml
+  type-handlers-package: cn.com.ty.lift.push.conf

+ 143 - 0
lift-push-pc/src/main/resources/logback-spring.xml

@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--debug="true" : 打印 logback 内部状态(默认当 logback 运行出错时才会打印内部状态 ), 配置该属性后打印条件如下(同时满足):
+    1、找到配置文件
+    2、配置文件是一个格式正确的xml文件 也可编程实现打印内部状态,
+    例如: LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory(); StatusPrinter.print(lc); -->
+<!-- scan="true" : 自动扫描该配置文件,若有修改则重新加载该配置文件 -->
+<!-- scanPeriod="30 seconds" : 配置自动扫面时间间隔
+    (单位可以是:milliseconds, seconds, minutes or hours,默认为:milliseconds),
+    默认为1分钟,scan="true"时该配置才会生效 -->
+        <!--https://raw.githubusercontent.com/enricopulatzo/logback-XSD/master/src/main/xsd/logback.xsd-->
+<configuration
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/enricopulatzo/logback-XSD/master/src/main/xsd/logback.xsd"
+        debug="false" scan="true" scanPeriod="30 seconds">
+
+    <property name="PROJECT" value="lift-push"/>
+    <property name="ROOT" value="logs/${PROJECT}/"/>
+    <property name="FILE_SIZE" value="10MB"/>
+    <property name="MAX_HISTORY" value="100"/>
+
+    <property name="PATTERN" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss} [%thread] %logger{30} - %m%n" />
+    <!-- 控制台打印 -->
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <!--是否支持ANSI color codes(类似linux中的shell脚本的输出字符串颜色控制代码)。默认为false。如果设置为true。例如:[31m 代表将前景色设置成红色。-->
+        <!--在windows中,需要提供"org.fusesource.jansi:jansi:1.9",而在linux,mac os x中默认支持。-->
+        <!--<withJansi>true</withJansi>-->
+        <encoder charset="utf-8">
+            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
+            <!--"%black", "%red", "%green","%yellow","%blue", "%magenta","%cyan", "%white", "%gray", "%boldRed","%boldGreen", -->
+            <!--"%boldYellow", "%boldBlue", "%boldMagenta""%boldCyan", "%boldWhite" and "%highlight"-->
+            <pattern>%highlight([%-5level]) %highlight(%d{yyyy-MM-dd HH:mm:ss}) %cyan([%thread]) %boldMagenta(%logger{30}) - %m%n
+            </pattern>
+        </encoder>
+    </appender>
+    <!-- ERROR 输入到文件,按日期和文件大小 -->
+    <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <encoder charset="utf-8">
+            <pattern>${PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${ROOT}%d/error.%i.log</fileNamePattern>
+            <maxHistory>${MAX_HISTORY}</maxHistory>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>${FILE_SIZE}</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+        </rollingPolicy>
+    </appender>
+
+    <!-- WARN 输入到文件,按日期和文件大小 -->
+    <appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <encoder charset="utf-8">
+            <pattern>${PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>WARN</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${ROOT}%d/warn.%i.log</fileNamePattern>
+            <maxHistory>${MAX_HISTORY}</maxHistory>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>${FILE_SIZE}</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+        </rollingPolicy>
+    </appender>
+
+    <!-- INFO 输入到文件,按日期和文件大小 -->
+    <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <encoder charset="utf-8">
+            <pattern>[%-5level] %d{${DATETIME}} [%thread] %logger{30} - %m%n</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>INFO</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${ROOT}%d/info.%i.log</fileNamePattern>
+            <maxHistory>${MAX_HISTORY}</maxHistory>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>${FILE_SIZE}</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+        </rollingPolicy>
+    </appender>
+    <!-- DEBUG 输入到文件,按日期和文件大小 -->
+    <appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <encoder charset="utf-8">
+            <pattern>${PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>DEBUG</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${ROOT}%d/debug.%i.log</fileNamePattern>
+            <maxHistory>${MAX_HISTORY}</maxHistory>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>${FILE_SIZE}</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+        </rollingPolicy>
+    </appender>
+    <!-- TRACE 输入到文件,按日期和文件大小 -->
+    <appender name="TRACE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <encoder charset="utf-8">
+            <pattern>${PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>TRACE</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <rollingPolicy
+                class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${ROOT}%d/trace.%i.log</fileNamePattern>
+            <maxHistory>${MAX_HISTORY}</maxHistory>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>${FILE_SIZE}</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+        </rollingPolicy>
+    </appender>
+
+    <!-- SQL相关日志输出-->
+    <logger name="org.apache.ibatis" level="INFO" additivity="true"/>
+    <logger name="org.mybatis.spring" level="INFO" additivity="false"/>
+    <logger name="com.github.miemiedev.mybatis.paginator" level="INFO" additivity="false"/>
+
+    <!-- Logger 根目录 -->
+    <root level="INFO">
+        <appender-ref ref="STDOUT"/>
+        <appender-ref ref="DEBUG"/>
+        <appender-ref ref="ERROR"/>
+        <appender-ref ref="WARN"/>
+        <appender-ref ref="INFO"/>
+        <appender-ref ref="TRACE"/>
+    </root>
+</configuration>

+ 133 - 0
lift-push-pc/src/main/resources/mapper/push/PushUserMapper.xml

@@ -0,0 +1,133 @@
+<?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="cn.com.ty.lift.push.push.mapper.PushUserMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="cn.com.ty.lift.common.model.PushUserInfo">
+        <id column="user_id" property="userId"/>
+        <result column="username" property="userName"/>
+        <result column="mobile" property="mobile"/>
+        <result column="device_model" property="deviceModel"/>
+        <result column="device_flag" property="deviceFlag"/>
+        <result column="role_name" property="roleName"/>
+    </resultMap>
+
+    <select id="listByUserId" resultMap="BaseResultMap">
+        SELECT
+            ui.user_id,
+            ui.NAME AS username,
+            ua.mobile,
+            ua.device_model,
+            ua.device_flag,
+            ro.NAME AS role_name
+        FROM
+            user_info ui
+            LEFT JOIN user_account ua ON ui.user_id = ua.user_id
+            LEFT JOIN user_role ur ON ui.user_id = ur.user_id
+            LEFT JOIN role ro ON ro.id = ur.role_id
+        <where>
+            <if test="mtCompanyId != null and mtCompanyId > 0">
+                AND ur.company_id = #{mtCompanyId}
+            </if>
+            <if test="userId != null and userId > 0">
+                AND ui.user_id = #{userId}
+            </if>
+        </where>
+    </select>
+
+    <select id="listByUserIds" resultMap="BaseResultMap">
+        SELECT
+            ui.user_id,
+            ui.NAME AS username,
+            ua.mobile,
+            ua.device_model,
+            ua.device_flag,
+            ro.NAME AS role_name
+        FROM
+            user_info ui
+            LEFT JOIN user_account ua ON ui.user_id = ua.user_id
+            LEFT JOIN user_role ur ON ui.user_id = ur.user_id
+            LEFT JOIN role ro ON ro.id = ur.role_id
+        <where>
+            <if test="mtCompanyId != null and mtCompanyId > 0">
+                AND ur.company_id = #{mtCompanyId}
+            </if>
+            <if test="userIds != null">
+                AND ui.user_id IN
+                <foreach collection="userIds" item="userId" open="(" separator="," close=")">
+                    #{userId, jdbcType=BIGINT}
+                </foreach>
+            </if>
+        </where>
+    </select>
+
+    <select id="listByRoleCodes" resultMap="BaseResultMap">
+        SELECT
+            ui.user_id,
+            ui.NAME AS username,
+            ua.mobile,
+            ua.device_model,
+            ua.device_flag,
+            ro.NAME AS role_name
+        FROM
+            user_info ui
+            LEFT JOIN user_account ua ON ui.user_id = ua.user_id
+            LEFT JOIN user_role ur ON ui.user_id = ur.user_id
+            LEFT JOIN role ro ON ro.id = ur.role_id
+        <where>
+            <if test="mtCompanyId != null and mtCompanyId > 0">
+                AND ur.company_id = #{mtCompanyId}
+            </if>
+            <if test="roleCodes != null">
+                AND ro.code IN
+                <foreach collection="roleCodes" item="roleCode" open="(" separator="," close=")">
+                    #{roleCode, jdbcType=VARCHAR}
+                </foreach>
+            </if>
+        </where>
+    </select>
+
+    <select id="listByRoleCode" resultMap="BaseResultMap">
+        SELECT
+        ui.user_id,
+        ui.NAME AS username,
+        ua.mobile,
+        ua.device_model,
+        ua.device_flag,
+        ro.NAME AS role_name
+        FROM
+        user_info ui
+        LEFT JOIN user_account ua ON ui.user_id = ua.user_id
+        LEFT JOIN user_role ur ON ui.user_id = ur.user_id
+        LEFT JOIN role ro ON ro.id = ur.role_id
+        <where>
+            <if test="mtCompanyId != null and mtCompanyId > 0">
+                AND ur.company_id = #{mtCompanyId}
+            </if>
+            <if test="roleCode != null and roleCode != ''">
+                AND ro.code = #{roleCode, jdbcType=VARCHAR}
+            </if>
+        </where>
+    </select>
+
+    <select id="pageByMtCompany" resultMap="BaseResultMap" parameterType="java.lang.Long">
+        SELECT
+            ui.user_id,
+            ui.NAME AS username,
+            ua.mobile,
+            ua.device_model,
+            ua.device_flag,
+            ro.NAME AS role_name
+        FROM
+            user_info ui
+            LEFT JOIN user_account ua ON ui.user_id = ua.user_id
+            LEFT JOIN user_role ur ON ui.user_id = ur.user_id
+            LEFT JOIN role ro ON ro.id = ur.role_id
+            LEFT JOIN mt_company_user mcu ON mcu.user_id = ui.user_id AND mcu.mt_company_id = ur.company_id
+        <where>
+            <if test="mtCompanyId != null and mtCompanyId > 0">
+                mcu.mt_company_id = #{mtCompanyId}
+            </if>
+        </where>
+    </select>
+</mapper>

+ 1 - 0
pom.xml

@@ -12,6 +12,7 @@
         <module>lift-batch-service</module>
         <module>lift-quan-service</module>
         <module>lift-push</module>
+        <module>lift-push-pc</module>
         <module>lift-iot</module>
         <module>lift-upload</module>
     </modules>