Browse Source

用户状态统计列表开发

gao.qiang 3 weeks atrás
parent
commit
8af2d1a1b5

+ 62 - 0
business-service/src/main/java/com/ozs/service/entity/UserStateStatistics.java

@@ -0,0 +1,62 @@
+package com.ozs.service.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.ozs.common.annotation.Excel;
+import com.ozs.common.core.domain.BaseEntity;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * 用户状态统计表
+ * </p>
+ *
+ * @author ozs
+ * @since 2023-02-17
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class UserStateStatistics extends BaseEntity implements Serializable{
+
+    private static final long serialVersionUID = 1L;
+
+    
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 用户ID
+     */
+    private String userId;
+
+    /**
+     * 昵称
+     */
+    private String nikeName;
+
+
+    /**
+     * 账号
+     */
+    private String userName;
+
+
+    /**
+     * 用户状态(1在线2离线)
+     */
+    private Integer isDisable;
+
+
+
+
+}

+ 34 - 0
business-service/src/main/java/com/ozs/service/entity/vo/UserStateStatisticsResp.java

@@ -0,0 +1,34 @@
+package com.ozs.service.entity.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ozs.common.annotation.Excel;
+import com.ozs.common.vo.PageVo;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @author Administrator
+ * 设备状态履历
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class UserStateStatisticsResp  implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @Excel(name = "姓名")
+    private String nikeName;
+    @Excel(name = "账号")
+    private String userName;
+    @Excel(name = "用户状态名称")
+    private String isDisableName;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime ;
+    private Integer faultNum;
+}

+ 9 - 0
business-service/src/main/java/com/ozs/service/mapper/BaseUserMapper.java

@@ -5,6 +5,7 @@ import com.ozs.service.entity.BaseUser;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
@@ -39,4 +40,12 @@ public interface BaseUserMapper extends BaseMapper<BaseUser> {
     List<String> getRoleName(@Param("userId")String userId);
 
     BaseUser getUserById(@Param("id")Long id);
+
+    List<String> getUserIdsLists();
+
+    List<BaseUser> getUsersByIds(@Param("userIds") List<String> userIds);
+
+    List<String> getUserIdsAll();
+
+    List<String> getUserLists();
 }

+ 24 - 0
business-service/src/main/java/com/ozs/service/mapper/UserStateStatisticsMapper.java

@@ -0,0 +1,24 @@
+package com.ozs.service.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ozs.service.entity.UserStateStatistics;
+import com.ozs.service.entity.vo.UserStateStatisticsResp;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+
+/**
+ * <p>
+ * 相机台账管理表 Mapper 接口
+ * </p>
+ *
+ * @author ozs
+ * @since 2023-02-17
+ */
+@Mapper
+public interface UserStateStatisticsMapper extends BaseMapper<UserStateStatistics> {
+
+    List<UserStateStatisticsResp> getUserStateStatistics(@Param("startTime")String startTime, @Param("endTime")String endTime,@Param("userState") Integer userState);
+}

+ 21 - 0
business-service/src/main/java/com/ozs/service/service/UserStateStatisticsService.java

@@ -0,0 +1,21 @@
+package com.ozs.service.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ozs.service.entity.UserStateStatistics;
+import com.ozs.service.entity.vo.UserStateStatisticsResp;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 相机台账管理表 服务类
+ * </p>
+ *
+ * @author ozs
+ * @since 2023-02-17
+ */
+public interface UserStateStatisticsService extends IService<UserStateStatistics> {
+    List<UserStateStatisticsResp> getUserStateStatistics(String startTime, String endTime,Integer userState );
+
+    List<UserStateStatisticsResp> exportUserStateStatistics(String startTime, String endTime,Integer userState);
+}

+ 31 - 0
business-service/src/main/java/com/ozs/service/service/impl/UserStateStatisticsServiceImpl.java

@@ -0,0 +1,31 @@
+package com.ozs.service.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ozs.service.entity.UserStateStatistics;
+import com.ozs.service.entity.vo.BaseDeviceResumeVo;
+import com.ozs.service.entity.vo.UserStateStatisticsResp;
+import com.ozs.service.mapper.UserStateStatisticsMapper;
+import com.ozs.service.service.UserStateStatisticsService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.List;
+
+@Slf4j
+@Service
+public class UserStateStatisticsServiceImpl extends ServiceImpl<UserStateStatisticsMapper, UserStateStatistics> implements UserStateStatisticsService {
+
+    @Autowired
+    private  UserStateStatisticsMapper userStateStatisticsMapper;
+    @Override
+    public List<UserStateStatisticsResp> getUserStateStatistics(String startTime, String endTime,Integer userState) {
+        return userStateStatisticsMapper.getUserStateStatistics(startTime, endTime, userState);
+    }
+
+    @Override
+    public List<UserStateStatisticsResp> exportUserStateStatistics(String startTime, String endTime,Integer userState) {
+        return userStateStatisticsMapper.getUserStateStatistics(startTime, endTime, userState);
+    }
+}

+ 53 - 0
business-service/src/main/resources/mapper/service/BaseUserMapper.xml

@@ -78,4 +78,57 @@
     <select id="getUserById" resultType="com.ozs.service.entity.BaseUser">
     select user_id userId, dept_id deptId, data_permission dataPermission,nick_name nickName,user_name userName from sys_user where id = #{id}
     </select>
+    <select id="getUserIdsLists" resultType="java.lang.String">
+        SELECT u.user_id
+        FROM sys_user u
+        WHERE u.status = 1
+          AND u.del_flag = 1
+          AND EXISTS (
+            SELECT 1
+            FROM sys_dept d1
+                     LEFT JOIN sys_dept d2 ON d1.dept_id = d2.parent_id AND d2.status = 1 AND d2.del_flag = 1
+            WHERE d1.status = 1
+              AND d1.del_flag = 1
+              AND (d1.dept_name = '工务部' OR d2.dept_name = '工务部')
+              AND u.dept_id IN (d1.dept_id, d2.dept_id)
+        );
+    </select>
+    <select id="getUsersByIds" resultType="com.ozs.service.entity.BaseUser">
+        SELECT user_id, user_name, nick_name
+        FROM sys_user
+        WHERE user_id IN
+        <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
+            #{userId}
+        </foreach>
+        AND status = 1 AND del_flag = 1
+    </select>
+    <select id="getUserIdsAll" resultType="java.lang.String">
+        SELECT u.user_id
+        FROM sys_user u
+        WHERE u.status = 1
+          AND u.del_flag = 1
+          AND EXISTS (
+            SELECT 1
+            FROM sys_dept d1
+                     LEFT JOIN sys_dept d2 ON d1.dept_id = d2.parent_id AND d2.status = 1 AND d2.del_flag = 1
+            WHERE d1.status = 1
+              AND d1.del_flag = 1
+              AND (d1.dept_name = '工务部' AND d2.dept_name LIKE '%工务段%')
+              AND u.dept_id IN (d1.dept_id, d2.dept_id)
+        );
+    </select>
+    <select id="getUserLists" resultType="java.lang.String">
+        SELECT u.user_id
+        FROM sys_user u
+        WHERE u.status = 1
+          AND u.del_flag = 1
+          AND EXISTS (
+            SELECT 1
+            FROM sys_dept d1
+            WHERE d1.status = 1
+              AND d1.del_flag = 1
+              AND d1.dept_name = '工务部'
+              AND u.dept_id IN (d1.dept_id)
+          )
+    </select>
 </mapper>

+ 38 - 0
business-service/src/main/resources/mapper/service/UserStateStatisticsMapper.xml

@@ -0,0 +1,38 @@
+<?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.ozs.service.mapper.UserStateStatisticsMapper">
+    <select id="getUserStateStatistics" resultType="com.ozs.service.entity.vo.UserStateStatisticsResp">
+        SELECT
+        u.nike_name AS nikeName,
+        u.user_name AS userName,
+        u.create_time AS createTime,
+        CASE
+        u.is_disable
+        WHEN 1 THEN
+        '在线'
+        WHEN 2 THEN
+        '离线'
+        END AS isDisableName,
+        COUNT(CASE WHEN u.is_disable = 2 THEN 1 END) OVER () AS faultNum
+        FROM
+        user_state_statistics u
+        <where>
+            <if test="userState != null and userState != 0">
+                AND u.is_disable =#{userState}
+            </if>
+            <if test="startTime != null and startTime != ''">
+                AND u.create_time &gt;=#{startTime}
+            </if>
+            <if test="startTime == null or startTime == ''">
+                AND u.create_time &gt;= DATE_SUB(NOW(), INTERVAL 72 HOUR)
+            </if>
+            <if test="endTime != null and endTime != ''">
+                AND u.create_time &lt;=#{endTime}
+            </if>
+            <if test="endTime == null and endTime == ''">
+                AND u.create_time &lt;= NOW()
+            </if>
+        </where>
+        ORDER BY u.create_time desc
+    </select>
+</mapper>

+ 54 - 0
hazard-admin/src/main/java/com/ozs/web/controller/accountmanagment/UserStateStatisticsController.java

@@ -0,0 +1,54 @@
+package com.ozs.web.controller.accountmanagment;
+
+import com.ozs.common.annotation.Log;
+import com.ozs.common.core.controller.BaseController;
+import com.ozs.common.core.domain.AjaxResult;
+import com.ozs.common.enums.BusinessType;
+import com.ozs.common.utils.poi.ExcelUtil;
+import com.ozs.service.entity.vo.UserStateStatisticsResp;
+import com.ozs.service.service.UserStateStatisticsService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+
+/**
+ * @author Administrator
+ */
+@RestController
+@RequestMapping("/service/userStateStatistics")
+@Slf4j
+public class UserStateStatisticsController extends BaseController {
+
+    @Autowired
+    private UserStateStatisticsService userStateStatisticsService;
+
+
+    @GetMapping("/getUserStateStatistics")
+    @Log(title = "用户状态履历列表", businessType = BusinessType.SELECT)
+    public AjaxResult getUserStateStatistics(@RequestParam(value = "userState", required = false) Integer userState,
+                                             @RequestParam(value = "startTime", required = false) String startTime,
+                                             @RequestParam(value = "endTime", required = false) String endTime) {
+
+        List<UserStateStatisticsResp> userStateStatisticsRespList = userStateStatisticsService.getUserStateStatistics(startTime, endTime, userState);
+        return AjaxResult.success(userStateStatisticsRespList);
+    }
+
+
+    @GetMapping("/exportUserStateStatistics")
+    @Log(title = "导出用户状态履历", businessType = BusinessType.EXPORT)
+    public void exportUserStateStatistics(HttpServletResponse response,
+                                          @RequestParam(value = "userState", required = false) Integer userState,
+                                          @RequestParam(value = "startTime", required = false) String startTime,
+                                          @RequestParam(value = "endTime", required = false) String endTime) {
+        List<UserStateStatisticsResp> userStateStatisticsRespList = userStateStatisticsService.exportUserStateStatistics(startTime, endTime, userState);
+        ExcelUtil<UserStateStatisticsResp> util = new ExcelUtil<>(UserStateStatisticsResp.class);
+        util.exportExcel(response, userStateStatisticsRespList, "用户状态履历数据");
+    }
+}

+ 8 - 1
hazard-admin/src/main/java/com/ozs/web/controller/websocket/WebSocketServer.java

@@ -6,11 +6,13 @@ import org.springframework.stereotype.Component;
 import javax.websocket.*;
 import javax.websocket.server.PathParam;
 import javax.websocket.server.ServerEndpoint;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.stream.Collectors;
 
 /**
  * @Author : sunhh
@@ -42,6 +44,11 @@ public class WebSocketServer {
         return session != null ? true : false;
     }
 
+    //获取所有在线用户
+    public List<String> getOnlineUser() {
+       return new ArrayList<>(sessionPool.keySet());
+    }
+
     /**
      * 链接成功调用的方法
      */
@@ -52,7 +59,7 @@ public class WebSocketServer {
             this.userId = userId;
             webSockets.add(this);
             sessionPool.put(userId, session);
-            System.out.println("链接成功" + webSockets.size());
+                System.out.println("链接成功" + webSockets.size());
             log.info("【websocket消息】有新的连接,总数为:" + webSockets.size());
         } catch (Exception e) {
         }

+ 180 - 0
hazard-admin/src/main/java/com/ozs/web/core/util/InsertUserStateUtil.java

@@ -0,0 +1,180 @@
+package com.ozs.web.core.util;
+
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ozs.service.entity.BaseUser;
+import com.ozs.service.entity.MsgWebPush;
+import com.ozs.service.entity.UserStateStatistics;
+import com.ozs.service.entity.vo.MsgAlarmResp;
+import com.ozs.service.mapper.BaseUserMapper;
+import com.ozs.service.service.UserStateStatisticsService;
+import com.ozs.web.controller.websocket.WebSocketServer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.ObjectUtils;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+
+@Configuration
+@EnableScheduling
+@EnableAsync
+public class InsertUserStateUtil {
+
+    private static final Logger log = LoggerFactory.getLogger(InsertUserStateUtil.class);
+
+    @Autowired
+    private WebSocketServer webSocketServer;
+    @Autowired
+    private BaseUserMapper baseUserMapper;
+    @Autowired
+    private UserStateStatisticsService userStateStatisticsService;
+
+    @Async
+//    @Scheduled(cron = "0 */1 * * * ?")
+    @Transactional
+    public void tableCreationAndDelete() {
+        // 获取在线用户和工务段用户
+        List<String> onlineUser = webSocketServer.getOnlineUser();
+        List<String> userIdList = baseUserMapper.getUserIdsLists();
+        //需要推送信息的用户
+        List<String> userIdsAll = baseUserMapper.getUserIdsAll();
+        //获取属于工段用户的用户ID
+        List<String>  userLists =baseUserMapper.getUserLists();
+
+
+        // 使用Set提高查找性能
+        Set<String> onlineSet = new HashSet<>(onlineUser);
+        Set<String> userSet = new HashSet<>(userIdList);
+        Set<String> userAll = new HashSet<>(userIdsAll);
+
+        // 计算差集和交集
+        Set<String> offlineUsers = userSet.stream()
+                .filter(userId -> !onlineSet.contains(userId))
+                .collect(Collectors.toSet());
+        Set<String> offlineUsersAll = userAll.stream()
+                .filter(userId -> !onlineSet.contains(userId))
+                .collect(Collectors.toSet());
+        Set<String> onlineUsers = onlineSet.stream()
+                .filter(userSet::contains)
+                .collect(Collectors.toSet());
+
+        // 批量查询用户信息,减少数据库查询
+        Map<String, BaseUser> userMap = baseUserMapper.getUsersByIds(
+                Stream.concat(offlineUsers.stream(), onlineUsers.stream())
+                        .collect(Collectors.toList())
+        ).stream().collect(Collectors.toMap(BaseUser::getUserId, user -> user));
+
+        // 批量查询最新用户状态
+        List<String> allUserIds = Stream.concat(offlineUsers.stream(), onlineUsers.stream())
+                .collect(Collectors.toList());
+        Map<String, UserStateStatistics> latestUserStateMap = getUserLatestStateMap(allUserIds);
+        log.info("用户状态 {}", latestUserStateMap);
+        // 处理离线用户
+        processUserStates(offlineUsers, userMap, latestUserStateMap, 2, userStateStatisticsService);
+
+        // 处理在线用户
+        processUserStates(onlineUsers, userMap, latestUserStateMap, 1, userStateStatisticsService);
+        // 推送信息
+        String data = objStr(offlineUsersAll.size());
+        log.info("准备推送数据: {}, 目标用户数: {}", data, userLists.size());
+        // 先批量检查在线状态,减少重复调用
+        List<String> onlineUserList = userLists.stream()
+                .filter(webSocketServer::userOnline)
+                .collect(Collectors.toList());
+
+        log.info("在线用户数: {}", onlineUserList.size());
+
+        // 批量推送
+        onlineUserList.parallelStream().forEach(userId -> {
+            try {
+                webSocketServer.sendOneMessage(userId, data);
+                log.info("推送成功 - 用户: {}", userId);
+            } catch (Exception e) {
+                log.error("推送消息给用户 {} 时发生异常: {}", userId, e.getMessage(), e);
+            }
+        });
+        log.info("推送完成,成功推送数: {}", onlineUserList.size());
+    }
+
+
+    /**
+     * 批量获取用户最新状态
+     */
+    private Map<String, UserStateStatistics> getUserLatestStateMap(List<String> userIds) {
+        if (ObjectUtils.isEmpty(userIds)) {
+            return new HashMap<>();
+        }
+
+        // 使用批量查询替代循环中的单个查询
+        List<UserStateStatistics> latestStates = userStateStatisticsService.list(
+                new LambdaQueryWrapper<UserStateStatistics>()
+                        .in(UserStateStatistics::getUserId, userIds)
+                        .orderByDesc(UserStateStatistics::getCreateTime)
+        );
+
+        // 按用户ID分组,取每个用户的最新状态
+        return latestStates.stream()
+                .collect(Collectors.toMap(
+                        UserStateStatistics::getUserId,
+                        Function.identity(),
+                        (existing, replacement) -> existing.getCreateTime().after(replacement.getCreateTime()) ? existing : replacement
+                ));
+    }
+
+    /**
+     * 处理用户状态更新
+     */
+    private void processUserStates(Set<String> userIds,
+                                   Map<String, BaseUser> userMap,
+                                   Map<String, UserStateStatistics> latestStateMap,
+                                   Integer targetState,
+                                   UserStateStatisticsService service) {
+        List<UserStateStatistics> toSave = new ArrayList<>();
+
+        for (String userId : userIds) {
+            UserStateStatistics latestState = latestStateMap.get(userId);
+            BaseUser user = userMap.get(userId);
+
+            if (user == null) continue;
+
+            // 判断是否需要新增记录
+            if (ObjectUtils.isEmpty(latestState) ||
+                    !targetState.equals(latestState.getIsDisable())) {
+
+                UserStateStatistics newState = new UserStateStatistics();
+                newState.setUserId(userId);
+                newState.setUserName(user.getUserName());
+                newState.setNikeName(user.getNickName());
+                newState.setIsDisable(targetState);
+                newState.setCreateTime(new Date());
+
+                toSave.add(newState);
+            }
+        }
+
+        // 批量保存
+        if (!toSave.isEmpty()) {
+            service.saveBatch(toSave);
+        }
+    }
+
+    private String objStr(Integer count) {
+        JSONObject obj = new JSONObject();
+        obj.put("data", count);
+        obj.put("code", 110);
+        log.info("【websocket消息】 单点消息:" + obj.toJSONString());
+        return obj.toJSONString();
+    }
+}