소스 검색

新增SSE协议测试接口

tsurumure 10 달 전
부모
커밋
3a44335e75

+ 2 - 4
src/main/java/com/backendsys/config/Security/filter/JwtAuthenticationFilter.java

@@ -138,10 +138,8 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
 
                 if (jwtUtil.isTokenValid(jwt, userDetails) && isTokenValid) {
                     // TODO 如果令牌有效,封装一个UsernamePasswordAuthenticationToken对象
-                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
-                        userDetails, null, userDetails.getAuthorities());
-                    authentication.setDetails(
-                            new WebAuthenticationDetailsSource().buildDetails(request));
+                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
+                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                     // 更新安全上下文的持有用户
                     SecurityContextHolder.getContext().setAuthentication(authentication);
                 }

+ 52 - 0
src/main/java/com/backendsys/modules/sse/controller/SseController.java

@@ -0,0 +1,52 @@
+package com.backendsys.modules.sse.controller;
+
+import com.backendsys.modules.sse.emitter.SseEmitterManager;
+import com.backendsys.modules.sse.utils.SseUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+@RestController
+public class SseController {
+
+    @Autowired
+    private SseUtil sseUtil;
+
+    /**
+     * [SSE] 消息监听
+     */
+    private final ExecutorService executor = Executors.newCachedThreadPool();
+
+    @GetMapping(value = "/api/sse/stream", produces = "text/event-stream")
+    public SseEmitter stream() {
+        SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
+        SseEmitterManager manager = SseEmitterManager.getInstance();
+        manager.addEmitter(emitter);
+        executor.execute(() -> {
+            try {
+                emitter.send(SseEmitter.event().data("success"));
+            } catch (IOException e) {
+                // 当所有事件发送完毕后,关闭连接
+                // emitter.complete();
+                // emitter.completeWithError(e);
+                manager.emitters.remove(emitter);
+            }
+        });
+        return emitter;
+    }
+
+    /**
+     * [SSE] 测试发送
+     */
+    @GetMapping("/api/sse/send")
+    public String send() {
+        sseUtil.send("send message!");
+        return "success";
+    }
+
+}

+ 28 - 0
src/main/java/com/backendsys/modules/sse/emitter/SseEmitterManager.java

@@ -0,0 +1,28 @@
+package com.backendsys.modules.sse.emitter;
+
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class SseEmitterManager {
+    // 单例实例
+    private static final SseEmitterManager INSTANCE = new SseEmitterManager();
+    // 存储SseEmitter的线程安全列表
+    public final CopyOnWriteArrayList<SseEmitter> emitters = new CopyOnWriteArrayList<>();
+    // 私有构造函数,防止外部直接实例化
+    private SseEmitterManager() {}
+    // 公共静态方法,获取单例实例
+    public static SseEmitterManager getInstance() {
+        return INSTANCE;
+    }
+    // 公共方法,供外部添加SseEmitter
+    public void addEmitter(SseEmitter emitter) {
+        this.emitters.add(emitter);
+        emitter.onTimeout(() -> this.emitters.remove(emitter));
+        emitter.onCompletion(() -> this.emitters.remove(emitter));
+    }
+    // 公共方法,供外部移除SseEmitter
+    public void removeEmitter(SseEmitter emitter) {
+        this.emitters.remove(emitter);
+    }
+}

+ 24 - 0
src/main/java/com/backendsys/modules/sse/utils/SseUtil.java

@@ -0,0 +1,24 @@
+package com.backendsys.modules.sse.utils;
+
+import com.backendsys.modules.sse.emitter.SseEmitterManager;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.io.IOException;
+
+@Component
+public class SseUtil {
+
+    // [SSE] 发送消息
+    public void send(String data) {
+        SseEmitterManager manager = SseEmitterManager.getInstance();
+        for (SseEmitter emitter : manager.emitters) {
+            try {
+                emitter.send(SseEmitter.event().data(data));
+            } catch (IOException e) {
+                manager.emitters.remove(emitter);
+            }
+        }
+    }
+
+}

+ 2 - 6
src/main/java/com/backendsys/service/v1/System/SysAuthServiceImpl.java

@@ -154,15 +154,11 @@ public class SysAuthServiceImpl implements SysAuthService {
 
         // [Redis] 删除旧 Redis Key
         String old_uuid = (String) sysUserDetail.get("last_login_uuid");
-        if (old_uuid != null) {
-            stringRedisTemplate.delete("token:id:" + old_uuid);
-        }
+        if (old_uuid != null) stringRedisTemplate.delete("token:id:" + old_uuid);
 
         // 3.判断用户 status 是否启用
         Object status = sysUserDetail.get("status");
-        if (status != null && (Integer) status == -1) {
-            throw new CustomException("该用户已被禁用");
-        }
+        if (status != null && (Integer) status == -1) throw new CustomException("该用户已被禁用");
 
         // 4.格式化 modules: [{ id: 1, module_code: "xxx" }] 转为 ["1.x.x", "2.x.x"] (减少 Token 长度)
         List<Map<String, Object>> roles = (List<Map<String, Object>>) sysUserDetail.get("roles");

+ 3 - 1
src/main/resources/application.yml

@@ -34,7 +34,9 @@ spring:
 mybatis-plus:
   global-config:
     banner: off
-  mapper-locations: classpath:com/backendsys/mapper/*.xml
+  mapper-locations:
+    - classpath:com/backendsys/mapper/*.xml
+    - classpath:mapper/*.xml
   # 开启控制台打印sql语句
 #  configuration:
 #    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

+ 371 - 0
src/main/resources/mapper/SysUserDao.xml

@@ -0,0 +1,371 @@
+<?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.backendsys.dao.System.SysUserDao">
+
+    <sql id="includeUser">
+        u.id id,
+        COALESCE(u.username, '') username,
+        COALESCE(u.phone, '') phone,
+        COALESCE(u.phone_area_code, '') phone_area_code,
+
+        uf.user_id user_id,
+        COALESCE(uf.nickname, '') nickname,
+        COALESCE(uf.email, '') email,
+        COALESCE(uf.gender, '') gender,
+        COALESCE(uf.avatar, '') AS avatar,
+        COALESCE(uf.last_login_ip, '') last_login_ip,
+        COALESCE(uf.last_login_uuid, '') last_login_uuid,
+        COALESCE(uf.last_login_time, '') last_login_time,
+        uf.is_super is_super,
+        uf.point_balance point_balance,
+        uf.invite_code invite_code,
+        uf.status status,
+        uf.audit_status audit_status,
+        uf.audit_note audit_note,
+        uf.create_time create_time,
+        uf.update_time update_time,
+        uf.del_flag del_flag
+    </sql>
+
+    <resultMap id="resultMapUserList" type="java.util.LinkedHashMap">
+        <id property="id" column="id" jdbcType="BIGINT" />
+        <result property="user_id" column="user_id" />
+        <result property="username" column="username" />
+        <result property="phone" column="phone" />
+        <result property="phone_area_code" column="phone_area_code" />
+        <result property="nickname" column="nickname" />
+        <result property="email" column="email" />
+        <result property="gender" column="gender" javaType="java.lang.Integer"/>
+        <result property="avatar" column="avatar" />
+        <result property="last_login_ip" column="last_login_ip" />
+        <result property="last_login_uuid" column="last_login_uuid" />
+        <result property="last_login_time" column="last_login_time" />
+        <result property="is_super" column="is_super" javaType="java.lang.Integer"/>
+        <result property="point_balance" column="point_balance" javaType="java.lang.Float"/>
+        <result property="invite_code" column="invite_code" />
+        <result property="status" column="status" javaType="java.lang.Integer"/>
+        <result property="audit_status" column="audit_status" javaType="java.lang.Integer"/>
+        <result property="audit_note" column="audit_note" />
+        <result property="create_time" column="create_time" />
+        <result property="update_time" column="update_time" />
+        <result property="del_flag" column="del_flag" javaType="java.lang.Integer"/>
+        <collection property="role_ids" javaType="java.util.List"
+                    ofType="java.lang.Long" column="role_id">
+            <id property="role_id" column="role_id" />
+        </collection>
+    </resultMap>
+    <resultMap id="resultMapUserDetail" type="java.util.LinkedHashMap">
+        <id property="id" column="id" jdbcType="BIGINT" />
+        <result property="user_id" column="user_id" />
+        <result property="username" column="username" />
+        <result property="phone" column="phone" />
+        <result property="phone_area_code" column="phone_area_code" />
+        <result property="nickname" column="nickname" />
+        <result property="email" column="email" />
+        <result property="gender" column="gender" javaType="java.lang.Integer"/>
+        <result property="avatar" column="avatar" />
+        <result property="last_login_ip" column="last_login_ip" />
+        <result property="last_login_uuid" column="last_login_uuid" />
+        <result property="last_login_time" column="last_login_time" />
+        <result property="is_super" column="is_super" javaType="java.lang.Integer"/>
+        <result property="point_balance" column="point_balance" javaType="java.lang.Float"/>
+        <result property="invite_code" column="invite_code" />
+        <result property="status" column="status" javaType="java.lang.Integer"/>
+        <result property="audit_status" column="audit_status" javaType="java.lang.Integer"/>
+        <result property="audit_note" column="audit_note" />
+        <result property="create_time" column="create_time" />
+        <result property="update_time" column="update_time" />
+        <result property="del_flag" column="del_flag" javaType="java.lang.Integer"/>
+        <collection property="roles" javaType="java.util.List" ofType="java.util.LinkedHashMap"
+                    select="queryRoleById" column="id">
+            <id property="id" column="id" />
+            <result property="role_name" column="role_name" />
+            <result property="sort" column="sort" />
+        </collection>
+    </resultMap>
+
+    <!-- [多对多,使用子查询] -->
+    <!-- [嵌套查询] 用户角色 / 角色模块 -->
+    <select id="queryRoleById" resultType="java.util.LinkedHashMap">
+        SELECT ur.id, ur.role_name,
+               GROUP_CONCAT(m.id) AS module_ids
+        FROM sys_user_role_relation urr
+                 LEFT JOIN sys_user_role ur ON urr.role_id = ur.id
+                 LEFT JOIN sys_user_role_module_relation mr ON urr.role_id = mr.role_id
+                 LEFT JOIN sys_user_role_module m ON mr.module_id = m.id
+        WHERE urr.user_id = #{userId}
+        GROUP BY ur.id
+        ORDER BY ur.sort ASC
+    </select>
+
+    <!--, um.module_code-->
+    <!--    <select id="queryModulesByRoleId" resultType="java.util.LinkedHashMap">-->
+    <!--        SELECT id, um.module_name-->
+    <!--        FROM sys_user_role_module um-->
+    <!--        WHERE um.id IN (-->
+    <!--            SELECT module_id FROM sys_user_role_module_relation-->
+    <!--            WHERE role_id IN-->
+    <!--            <foreach collection="roles" item="role" open="(" separator="," close=")">-->
+    <!--                #{role.id}-->
+    <!--            </foreach>-->
+    <!--        )-->
+    <!--    </select>-->
+    <!--WHERE role_id = #{roleId}-->
+
+    <!-- 根据 ID 或 账号 查询是否存在 (用于校验密码是否正确) -->
+    <select id="queryUserByIdOrName" resultType="java.util.LinkedHashMap">
+        SELECT id, username, phone, password FROM sys_user
+        <where>
+            <if test="username != null and username != ''">
+                AND username = #{username}
+            </if>
+            <if test="phone != null and phone != ''">
+                AND phone = #{phone}
+            </if>
+            <if test="phone_area_code != null and phone_area_code != ''">
+                AND phone_area_code = #{phone_area_code}
+            </if>
+            <if test="id != null and id != ''">
+                AND id = #{id}
+            </if>
+        </where>
+    </select>
+    <!-- 逻辑删除时,查询加这一段判断
+    AND id IN (
+        SELECT user_id FROM sys_user_info WHERE del_flag = -1
+    )
+    -->
+
+    <!-- 查询 用户列表 (分页) (嵌套查询) -->
+    <select id="queryUserList" resultMap="resultMapUserList">
+        SELECT
+        <include refid="includeUser" />,
+        urr.role_id role_id
+        FROM sys_user u
+        LEFT JOIN sys_user_info uf ON u.id = uf.user_id
+        LEFT JOIN sys_user_role_relation urr ON uf.user_id = urr.user_id
+        <where>
+            AND uf.del_flag != 1
+            <if test="username != null and username != ''">
+                AND u.username LIKE CONCAT('%', #{username}, '%')
+            </if>
+            <if test="phone != null and phone != ''">
+                AND u.phone LIKE CONCAT('%', #{phone}, '%')
+            </if>
+            <if test="phone_area_code != null and phone_area_code != ''">
+                AND u.phone_area_code = #{phone_area_code}
+            </if>
+            <if test="nickname != null and nickname != ''">
+                AND uf.nickname LIKE CONCAT('%', #{nickname}, '%')
+            </if>
+            <if test="email != null and email != ''">
+                AND uf.email LIKE CONCAT('%', #{email}, '%')
+            </if>
+            <if test="status != null and status != ''">
+                AND uf.status = #{status}
+            </if>
+            <if test="audit_status != null and audit_status != ''">
+                AND uf.audit_status = #{audit_status}
+            </if>
+            <if test="role_id != null and role_id != ''">
+                AND urr.role_id IN
+                <foreach collection="role_id" item="role" open="(" separator="," close=")">
+                    #{role}
+                </foreach>
+            </if>
+            <if test="is_super != null and is_super != ''">
+                AND uf.is_super = #{is_super}
+            </if>
+            AND uf.id != 1
+        </where>
+        ORDER BY uf.create_time DESC
+    </select>
+
+    <!-- 查询 用户详情 -->
+    <select id="queryUserDetail" resultMap="resultMapUserDetail">
+        SELECT
+        <include refid="includeUser" />
+        FROM sys_user u
+        LEFT JOIN sys_user_info uf ON u.id = uf.user_id
+        WHERE u.id = #{user_id}
+        AND uf.del_flag = -1
+    </select>
+
+    <select id="queryUserUUIDSByIds" resultType="java.util.LinkedHashMap">
+        SELECT last_login_uuid
+        FROM sys_user_info
+        WHERE user_id IN
+        <foreach item="id" collection="ids" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </select>
+
+
+    <!-- 查询当前在线用户的信息 -->
+    <select id="queryUserWithLogined" resultType="java.util.LinkedHashMap">
+        SELECT
+        u.id id,
+        COALESCE(u.username, '') username,
+        COALESCE(u.phone, '') phone,
+        COALESCE(u.phone_area_code, '') phone_area_code,
+
+        COALESCE(uf.nickname, '') nickname,
+        COALESCE(uf.email, '') email,
+        COALESCE(uf.avatar, '') avatar,
+        uf.is_super is_super,
+        uf.status status,
+        uf.audit_status audit_status,
+        uf.audit_note audit_note
+        FROM sys_user u
+        LEFT JOIN sys_user_info uf ON u.id = uf.user_id
+        WHERE uf.last_login_uuid IN
+        <foreach collection="last_login_uuids" item="last_login_uuid" open="(" separator="," close=")">
+            #{last_login_uuid}
+        </foreach>
+    </select>
+
+    <!-- 创建用户 -->
+    <insert id="insertUser" parameterType="com.backendsys.entity.v1.System.SysUserDTO"
+            useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO sys_user (
+        password
+        <if test="username != null and username != ''">, username</if>
+        <if test="phone != null and phone != ''">, phone</if>
+        <if test="phone_area_code != null and phone_area_code != ''">, phone_area_code</if>
+        )
+        VALUES (
+        #{password}
+        <if test="username != null and username != ''">, #{username}</if>
+        <if test="phone != null and phone != ''">, #{phone}</if>
+        <if test="phone_area_code != null and phone_area_code != ''">, #{phone_area_code}</if>
+        );
+        SET @last_user_id = LAST_INSERT_ID();
+
+        INSERT INTO sys_user_info (user_id
+        <if test="nickname != null and nickname != ''">, nickname</if>
+        <if test="email != null and email != ''">, email</if>
+        <if test="gender != null and gender != ''">, gender</if>
+        <if test="avatar != null and avatar != ''">, avatar</if>
+        <if test="status != null and status != ''">, status</if>
+        <if test="invite_code != null and invite_code != ''">, invite_code</if>
+        ) VALUES (@last_user_id
+        <if test="nickname != null and nickname != ''">, #{nickname}</if>
+        <if test="email != null and email != ''">, #{email}</if>
+        <if test="gender != null and gender != ''">, #{gender}</if>
+        <if test="avatar != null and avatar != ''">, #{avatar}</if>
+        <if test="status != null and status != ''">, #{status}</if>
+        <if test="invite_code != null and invite_code != ''">, #{invite_code}</if>
+        );
+
+        <if test="roles != null and roles.size() > 0">
+            INSERT INTO sys_user_role_relation (user_id, role_id)
+            VALUES
+            <foreach collection="roles" item="role" separator=",">
+                (@last_user_id, #{role.id})
+            </foreach>
+        </if>
+    </insert>
+
+    <!-- 编辑 用户信息 -->
+    <update id="updateUserInfo" parameterType="com.backendsys.entity.v1.System.SysUserDTO">
+        UPDATE sys_user_info SET
+        <trim suffixOverrides="," suffix=" ">
+            <if test="nickname != null and nickname != ''">nickname = #{nickname},</if>
+            <if test="email != null and email != ''">email = #{email},</if>
+            <if test="gender != null and gender != ''">gender = #{gender},</if>
+            <if test="avatar != null and avatar != ''">avatar = #{avatar},</if>
+            <if test="point_balance != null">point_balance = #{point_balance},</if>
+            <if test="invite_code != null">invite_code = #{invite_code},</if>
+            <if test="status != null and status != ''">status = #{status},</if>
+            <if test="last_login_ip != null">last_login_ip = #{last_login_ip},</if>
+            <if test="last_login_uuid != null">last_login_uuid = #{last_login_uuid},</if>
+            <if test="last_login_time != null">last_login_time = #{last_login_time},</if>
+        </trim>
+        WHERE user_id = #{user_id};
+
+        <!--判断是否有新的数据需要插入-->
+        <if test="roles != null and roles.size() > 0">
+            <!--删除符合条件的数据-->
+            DELETE FROM sys_user_role_relation WHERE user_id = #{user_id};
+            <!--插入-->
+            INSERT INTO sys_user_role_relation (user_id, role_id)
+            VALUES
+            <foreach collection="roles" item="role" separator=",">
+                (#{user_id}, #{role.id})
+            </foreach>
+        </if>
+    </update>
+    <!--删掉全部关联,再重新关联-->
+
+
+
+    <!-- 审核用户 -->
+    <update id="auditUser" parameterType="com.backendsys.entity.v1.System.SysUserDTO">
+        UPDATE sys_user_info SET
+        <trim suffixOverrides="," suffix=" ">
+            <if test="audit_status != null and audit_status != ''">audit_status = #{audit_status},</if>
+            <if test="audit_note != null and audit_note != ''">audit_note = #{audit_note},</if>
+        </trim>
+        WHERE user_id = #{user_id};
+    </update>
+
+
+    <!-- 查询 用户密码 -->
+    <select id="queryUserPassword" parameterType="com.backendsys.entity.v1.System.SysUserDTO">
+        SELECT password FROM sys_user WHERE id = #{user_id}
+    </select>
+    <!-- 编辑 用户密码 -->
+    <update id="updateUserPassword" parameterType="com.backendsys.entity.v1.System.SysUserDTO">
+        UPDATE sys_user SET password = #{password} WHERE id = #{user_id}
+    </update>
+
+    <!-- 编辑 用户积分 -->
+    <update id="updateUserPoint" parameterType="com.backendsys.entity.v1.System.SysUserPointsDTO">
+        UPDATE sys_user_info SET point_balance = #{point_balance} WHERE user_id = #{target_user_id}
+    </update>
+    <!-- 查询 用户积分 -->
+    <select id="queryUserPoint" resultType="java.util.LinkedHashMap">
+        SELECT point_balance FROM sys_user_info WHERE user_id = #{target_user_id}
+    </select>
+
+    <!-- 创建 用户积分操作记录 -->
+    <insert id="insertUserPointHistory" parameterType="com.backendsys.entity.v1.System.SysUserPointsDTO"
+            useGeneratedKeys="true" keyProperty="target_user_id">
+        INSERT INTO sys_user_points_history (
+        target_user_id, point_adjustment, point_balance, activity_type, activity_type_description, operator_type
+        <if test="activity_detail != null and activity_detail != ''">, activity_detail</if>
+        <if test="operator_user_id != null and operator_user_id != ''">, operator_user_id</if>
+        ) VALUES (
+        #{target_user_id}, #{point_adjustment}, #{point_balance}, #{activity_type}, #{activity_type_description},  #{operator_type}
+        <if test="activity_detail != null and activity_detail != ''">, #{activity_detail}</if>
+        <if test="operator_user_id != null and operator_user_id != ''">, #{operator_user_id}</if>
+        )
+    </insert>
+
+    <!-- 删除用户 (物理) -->
+    <delete id="deleteUser" parameterType="java.lang.Long">
+        DELETE u, ui
+        FROM sys_user u
+        LEFT JOIN sys_user_info ui ON u.id = ui.user_id
+        WHERE u.id = #{id}
+    </delete>
+
+    <!-- 删除用户 (批量) -->
+    <delete id="deleteUserBatch" parameterType="java.lang.Long">
+        DELETE u, ui
+        FROM sys_user u
+        LEFT JOIN sys_user_info ui ON u.id = ui.user_id
+        WHERE u.id IN
+        <foreach collection="ids" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <!-- 删除用户 (逻辑) -->
+    <update id="deactivateUser" parameterType="java.lang.Long">
+        UPDATE sys_user_info
+        SET del_flag = 1
+        WHERE user_id = #{id}
+    </update>
+
+</mapper>