消息模块、爬虫

This commit is contained in:
2025-11-13 19:00:27 +08:00
parent 2982d53800
commit e20a7755f8
85 changed files with 8637 additions and 201 deletions

View File

@@ -0,0 +1,44 @@
# Message模块配置文件
# 此配置文件定义message模块独立运行时的配置
# 如需作为微服务集成请参考admin模块的bootstrap.yml配置
# 消息模块配置
message:
# 定时任务扫描配置
scheduler:
# 扫描频率cron表达式默认每分钟扫描一次
cron: "0 * * * * ?"
# 是否启用定时任务扫描
enabled: true
# 消息发送配置
send:
# 异步发送线程池大小
thread-pool-size: 10
# 批量发送每批次大小
batch-size: 100
# Spring Boot配置如果需要独立运行
# server:
# port: 8087
#
# spring:
# application:
# name: message-service
#
# datasource:
# url: jdbc:mysql://localhost:3306/school_news?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
# username: root
# password: your_password
# driver-class-name: com.mysql.cj.jdbc.Driver
# hikari:
# maximum-pool-size: 20
# minimum-idle: 5
# connection-timeout: 30000
#
# mybatis-plus:
# mapper-locations: classpath:mapper/*.xml
# type-aliases-package: org.xyzh.common.dto.message
# configuration:
# map-underscore-to-camel-case: true
# log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl

View File

@@ -0,0 +1,279 @@
<?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="org.xyzh.message.mapper.MessageMapper">
<!-- Result Map -->
<resultMap id="BaseResultMap" type="org.xyzh.common.dto.message.TbSysMessage">
<id column="id" property="id" jdbcType="VARCHAR"/>
<result column="message_id" property="messageID" jdbcType="VARCHAR"/>
<result column="title" property="title" jdbcType="VARCHAR"/>
<result column="content" property="content" jdbcType="VARCHAR"/>
<result column="message_type" property="messageType" jdbcType="VARCHAR"/>
<result column="priority" property="priority" jdbcType="VARCHAR"/>
<result column="sender_id" property="senderID" jdbcType="VARCHAR"/>
<result column="sender_name" property="senderName" jdbcType="VARCHAR"/>
<result column="sender_dept_id" property="senderDeptID" jdbcType="VARCHAR"/>
<result column="sender_dept_name" property="senderDeptName" jdbcType="VARCHAR"/>
<result column="send_mode" property="sendMode" jdbcType="VARCHAR"/>
<result column="scheduled_time" property="scheduledTime" jdbcType="TIMESTAMP"/>
<result column="actual_send_time" property="actualSendTime" jdbcType="TIMESTAMP"/>
<result column="status" property="status" jdbcType="VARCHAR"/>
<result column="target_user_count" property="targetUserCount" jdbcType="INTEGER"/>
<result column="sent_count" property="sentCount" jdbcType="INTEGER"/>
<result column="success_count" property="successCount" jdbcType="INTEGER"/>
<result column="failed_count" property="failedCount" jdbcType="INTEGER"/>
<result column="read_count" property="readCount" jdbcType="INTEGER"/>
<result column="retry_count" property="retryCount" jdbcType="INTEGER"/>
<result column="max_retry_count" property="maxRetryCount" jdbcType="INTEGER"/>
<result column="last_error" property="lastError" jdbcType="VARCHAR"/>
<result column="creator" property="creator" jdbcType="VARCHAR"/>
<result column="updater" property="updater" jdbcType="VARCHAR"/>
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
<result column="delete_time" property="deleteTime" jdbcType="TIMESTAMP"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/>
</resultMap>
<resultMap id="MessageVOMap" type="org.xyzh.common.dto.message.MessageVO">
<id column="id" property="id" jdbcType="VARCHAR"/>
<result column="message_id" property="messageID" jdbcType="VARCHAR"/>
<result column="title" property="title" jdbcType="VARCHAR"/>
<result column="content" property="content" jdbcType="VARCHAR"/>
<result column="message_type" property="messageType" jdbcType="VARCHAR"/>
<result column="priority" property="priority" jdbcType="VARCHAR"/>
<result column="sender_id" property="senderID" jdbcType="VARCHAR"/>
<result column="sender_name" property="senderName" jdbcType="VARCHAR"/>
<result column="sender_dept_id" property="senderDeptID" jdbcType="VARCHAR"/>
<result column="sender_dept_name" property="senderDeptName" jdbcType="VARCHAR"/>
<result column="send_mode" property="sendMode" jdbcType="VARCHAR"/>
<result column="scheduled_time" property="scheduledTime" jdbcType="TIMESTAMP"/>
<result column="actual_send_time" property="actualSendTime" jdbcType="TIMESTAMP"/>
<result column="status" property="status" jdbcType="VARCHAR"/>
<result column="target_user_count" property="targetUserCount" jdbcType="INTEGER"/>
<result column="sent_count" property="sentCount" jdbcType="INTEGER"/>
<result column="success_count" property="successCount" jdbcType="INTEGER"/>
<result column="failed_count" property="failedCount" jdbcType="INTEGER"/>
<result column="read_count" property="readCount" jdbcType="INTEGER"/>
<result column="retry_count" property="retryCount" jdbcType="INTEGER"/>
<result column="max_retry_count" property="maxRetryCount" jdbcType="INTEGER"/>
<result column="last_error" property="lastError" jdbcType="VARCHAR"/>
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
</resultMap>
<!-- 分页查询消息列表(带权限过滤) -->
<select id="selectMessagePage" resultMap="MessageVOMap">
SELECT
m.*
FROM tb_sys_message m
WHERE m.deleted = 0
<if test="filter != null">
<if test="filter.title != null and filter.title != ''">
AND m.title LIKE CONCAT('%', #{filter.title}, '%')
</if>
<if test="filter.messageType != null and filter.messageType != ''">
AND m.message_type = #{filter.messageType}
</if>
<if test="filter.status != null and filter.status != ''">
AND m.status = #{filter.status}
</if>
<if test="filter.sendMode != null and filter.sendMode != ''">
AND m.send_mode = #{filter.sendMode}
</if>
<if test="filter.priority != null and filter.priority != ''">
AND m.priority = #{filter.priority}
</if>
</if>
<!-- 权限过滤:只能查看自己部门及子部门的消息 -->
AND (
m.sender_dept_id = #{currentUserDeptID}
OR m.sender_dept_id IN (
WITH RECURSIVE dept_tree AS (
SELECT dept_id FROM tb_sys_dept
WHERE dept_id = #{currentUserDeptID} AND deleted = 0
UNION ALL
SELECT d.dept_id FROM tb_sys_dept d
INNER JOIN dept_tree dt ON d.parent_id = dt.dept_id
WHERE d.deleted = 0
)
SELECT dept_id FROM dept_tree
)
)
ORDER BY m.create_time DESC
</select>
<!-- 查询消息详情 -->
<select id="selectMessageDetail" resultMap="MessageVOMap">
SELECT *
FROM tb_sys_message
WHERE message_id = #{messageID}
AND deleted = 0
</select>
<!-- 查询待发送的定时消息 -->
<select id="selectPendingScheduledMessages" resultMap="BaseResultMap">
SELECT *
FROM tb_sys_message
WHERE status = 'pending'
AND send_mode = 'scheduled'
AND scheduled_time <![CDATA[ <= ]]> #{currentTime}
AND deleted = 0
ORDER BY scheduled_time ASC
LIMIT 100
</select>
<!-- CAS更新消息状态 -->
<update id="compareAndSetStatus">
UPDATE tb_sys_message
SET status = #{newStatus},
update_time = NOW()
WHERE message_id = #{messageID}
AND status = #{expectedStatus}
AND deleted = 0
</update>
<!-- 更新消息统计信息 -->
<update id="updateStatistics">
UPDATE tb_sys_message
SET sent_count = #{sentCount},
success_count = #{successCount},
failed_count = #{failedCount},
update_time = NOW()
WHERE message_id = #{messageID}
AND deleted = 0
</update>
<!-- 更新已读数量 -->
<update id="incrementReadCount">
UPDATE tb_sys_message
SET read_count = read_count + 1,
update_time = NOW()
WHERE message_id = #{messageID}
AND deleted = 0
</update>
<!-- 根据消息ID查询消息 -->
<select id="selectMessageById" resultMap="BaseResultMap">
SELECT *
FROM tb_sys_message
WHERE message_id = #{messageID}
AND deleted = 0
</select>
<!-- 插入消息 -->
<insert id="insertMessage">
INSERT INTO tb_sys_message
(id, message_id, title, content, message_type, priority, sender_id, sender_dept_id,
send_mode, scheduled_time, status, target_user_count, retry_count, max_retry_count,
creator, create_time, update_time, deleted)
VALUES
(#{message.id}, #{message.messageID}, #{message.title}, #{message.content},
#{message.messageType}, #{message.priority}, #{message.senderID}, #{message.senderDeptID},
#{message.sendMode}, #{message.scheduledTime}, #{message.status}, #{message.targetUserCount},
#{message.retryCount}, #{message.maxRetryCount}, #{message.creator}, #{message.createTime},
#{message.updateTime}, #{message.deleted})
</insert>
<!-- 更新消息 -->
<update id="updateMessage">
UPDATE tb_sys_message
<set>
<if test="message.title != null and message.title != ''">
title = #{message.title},
</if>
<if test="message.content != null">
content = #{message.content},
</if>
<if test="message.messageType != null and message.messageType != ''">
message_type = #{message.messageType},
</if>
<if test="message.priority != null and message.priority != ''">
priority = #{message.priority},
</if>
<if test="message.sendMode != null and message.sendMode != ''">
send_mode = #{message.sendMode},
</if>
<if test="message.scheduledTime != null">
scheduled_time = #{message.scheduledTime},
</if>
<if test="message.actualSendTime != null">
actual_send_time = #{message.actualSendTime},
</if>
<if test="message.status != null and message.status != ''">
status = #{message.status},
</if>
<if test="message.targetUserCount != null">
target_user_count = #{message.targetUserCount},
</if>
<if test="message.retryCount != null">
retry_count = #{message.retryCount},
</if>
<if test="message.maxRetryCount != null">
max_retry_count = #{message.maxRetryCount},
</if>
<if test="message.lastError != null">
last_error = #{message.lastError},
</if>
<if test="message.updater != null and message.updater != ''">
updater = #{message.updater},
</if>
<if test="message.updateTime != null">
update_time = #{message.updateTime},
</if>
</set>
WHERE message_id = #{message.messageID}
AND deleted = 0
</update>
<!-- 删除消息(逻辑删除) -->
<update id="deleteMessage">
UPDATE tb_sys_message
SET deleted = 1,
delete_time = NOW(),
update_time = NOW()
WHERE message_id = #{messageID}
AND deleted = 0
</update>
<!-- 统计消息总数(带权限过滤) -->
<select id="countMessage" resultType="java.lang.Integer">
SELECT COUNT(*)
FROM tb_sys_message m
WHERE m.deleted = 0
<if test="filter != null">
<if test="filter.title != null and filter.title != ''">
AND m.title LIKE CONCAT('%', #{filter.title}, '%')
</if>
<if test="filter.messageType != null and filter.messageType != ''">
AND m.message_type = #{filter.messageType}
</if>
<if test="filter.status != null and filter.status != ''">
AND m.status = #{filter.status}
</if>
<if test="filter.sendMode != null and filter.sendMode != ''">
AND m.send_mode = #{filter.sendMode}
</if>
<if test="filter.priority != null and filter.priority != ''">
AND m.priority = #{filter.priority}
</if>
</if>
<!-- 权限过滤:只能查看自己部门及子部门的消息 -->
AND (
m.sender_dept_id = #{currentUserDeptID}
OR m.sender_dept_id IN (
WITH RECURSIVE dept_tree AS (
SELECT dept_id FROM tb_sys_dept
WHERE dept_id = #{currentUserDeptID} AND deleted = 0
UNION ALL
SELECT d.dept_id FROM tb_sys_dept d
INNER JOIN dept_tree dt ON d.parent_id = dt.dept_id
WHERE d.deleted = 0
)
SELECT dept_id FROM dept_tree
)
)
</select>
</mapper>

View File

@@ -0,0 +1,54 @@
<?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="org.xyzh.message.mapper.MessageTargetMapper">
<!-- Result Map -->
<resultMap id="BaseResultMap" type="org.xyzh.common.dto.message.TbSysMessageTarget">
<id column="id" property="id" jdbcType="VARCHAR"/>
<result column="message_id" property="messageID" jdbcType="VARCHAR"/>
<result column="send_method" property="sendMethod" jdbcType="VARCHAR"/>
<result column="target_type" property="targetType" jdbcType="VARCHAR"/>
<result column="target_id" property="targetID" jdbcType="VARCHAR"/>
<result column="target_name" property="targetName" jdbcType="VARCHAR"/>
<result column="scope_dept_id" property="scopeDeptID" jdbcType="VARCHAR"/>
<result column="creator" property="creator" jdbcType="VARCHAR"/>
<result column="updater" property="updater" jdbcType="VARCHAR"/>
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
<result column="delete_time" property="deleteTime" jdbcType="TIMESTAMP"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/>
</resultMap>
<!-- 根据消息ID查询接收对象列表 -->
<select id="selectByMessageID" resultMap="BaseResultMap">
SELECT *
FROM tb_sys_message_target
WHERE message_id = #{messageID}
AND deleted = 0
ORDER BY create_time ASC
</select>
<!-- 批量插入接收对象 -->
<insert id="batchInsert">
INSERT INTO tb_sys_message_target
(id, message_id, send_method, target_type, target_id, target_name, scope_dept_id, creator, create_time, update_time, deleted)
VALUES
<foreach collection="targets" item="item" separator=",">
(#{item.id}, #{item.messageID}, #{item.sendMethod}, #{item.targetType}, #{item.targetID},
#{item.targetName}, #{item.scopeDeptID}, #{item.creator}, NOW(), NOW(), 0)
</foreach>
</insert>
<!-- 根据消息ID删除接收对象 -->
<update id="deleteByMessageID">
UPDATE tb_sys_message_target
SET deleted = 1,
delete_time = NOW(),
update_time = NOW()
WHERE message_id = #{messageID}
AND deleted = 0
</update>
</mapper>

View File

@@ -0,0 +1,352 @@
<?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="org.xyzh.message.mapper.MessageUserMapper">
<!-- Result Map -->
<resultMap id="BaseResultMap" type="org.xyzh.common.dto.message.TbSysMessageUser">
<id column="id" property="id" jdbcType="VARCHAR"/>
<result column="message_id" property="messageID" jdbcType="VARCHAR"/>
<result column="user_id" property="userID" jdbcType="VARCHAR"/>
<result column="send_method" property="sendMethod" jdbcType="VARCHAR"/>
<result column="is_read" property="isRead" jdbcType="BOOLEAN"/>
<result column="read_time" property="readTime" jdbcType="TIMESTAMP"/>
<result column="send_status" property="sendStatus" jdbcType="VARCHAR"/>
<result column="fail_reason" property="failReason" jdbcType="VARCHAR"/>
<result column="creator" property="creator" jdbcType="VARCHAR"/>
<result column="updater" property="updater" jdbcType="VARCHAR"/>
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
<result column="delete_time" property="deleteTime" jdbcType="TIMESTAMP"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/>
</resultMap>
<resultMap id="MessageUserVOMap" type="org.xyzh.common.dto.message.MessageUserVO">
<id column="id" property="id" jdbcType="VARCHAR"/>
<result column="message_id" property="messageID" jdbcType="VARCHAR"/>
<result column="user_id" property="userID" jdbcType="VARCHAR"/>
<result column="username" property="username" jdbcType="VARCHAR"/>
<result column="full_name" property="fullName" jdbcType="VARCHAR"/>
<result column="dept_id" property="deptID" jdbcType="VARCHAR"/>
<result column="name" property="deptName" jdbcType="VARCHAR"/>
<result column="send_method" property="sendMethod" jdbcType="VARCHAR"/>
<result column="is_read" property="isRead" jdbcType="BOOLEAN"/>
<result column="read_time" property="readTime" jdbcType="TIMESTAMP"/>
<result column="send_status" property="sendStatus" jdbcType="VARCHAR"/>
<result column="fail_reason" property="failReason" jdbcType="VARCHAR"/>
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
<result column="title" property="title" jdbcType="VARCHAR"/>
<result column="content" property="content" jdbcType="VARCHAR"/>
<result column="message_type" property="messageType" jdbcType="VARCHAR"/>
<result column="priority" property="priority" jdbcType="VARCHAR"/>
<result column="sender_name" property="senderName" jdbcType="VARCHAR"/>
<result column="sender_dept_name" property="senderDeptName" jdbcType="VARCHAR"/>
<result column="actual_send_time" property="actualSendTime" jdbcType="TIMESTAMP"/>
</resultMap>
<!-- 根据消息ID查询用户消息列表 -->
<select id="selectByMessageID" resultMap="MessageUserVOMap">
SELECT
mu.*,
u.username,
ui.full_name as full_name,
d.dept_id as dept_id,
d.name as deptName
FROM tb_sys_message_user mu
LEFT JOIN tb_sys_user u ON mu.user_id = u.id
LEFT JOIN tb_sys_user_info ui ON u.id = ui.user_id
LEFT JOIN tb_sys_user_dept_role udr ON u.id = udr.user_id AND udr.deleted = 0
LEFT JOIN tb_sys_dept d ON udr.dept_id = d.dept_id AND d.deleted = 0
WHERE mu.message_id = #{messageID}
AND mu.deleted = 0
ORDER BY mu.create_time DESC
</select>
<!-- 批量插入用户消息 -->
<insert id="batchInsert">
INSERT INTO tb_sys_message_user
(id, message_id, user_id, send_method, is_read, send_status, creator, create_time, update_time, deleted)
VALUES
<foreach collection="userMessages" item="item" separator=",">
(#{item.id}, #{item.messageID}, #{item.userID}, #{item.sendMethod},
0, 'pending', #{item.creator}, NOW(), NOW(), 0)
</foreach>
</insert>
<!-- 分页查询当前用户的消息列表 -->
<select id="selectMyMessages" resultMap="MessageUserVOMap">
SELECT
mu.*,
m.title,
m.content,
m.message_type as message_type,
m.priority,
m.sender_name as sender_name,
m.sender_dept_name as sender_dept_name,
m.actual_send_time as actual_send_time
FROM tb_sys_message_user mu
INNER JOIN tb_sys_message m ON mu.message_id = m.message_id
WHERE mu.user_id = #{userID}
AND mu.deleted = 0
AND m.deleted = 0
<if test="filter != null">
<if test="filter.isRead != null">
AND mu.is_read = #{filter.isRead}
</if>
<if test="filter.sendMethod != null and filter.sendMethod != ''">
AND mu.send_method = #{filter.sendMethod}
</if>
</if>
ORDER BY m.actual_send_time DESC, mu.create_time DESC
</select>
<!-- 查询当前用户的消息详情 -->
<select id="selectMyMessageDetail" resultMap="MessageUserVOMap">
SELECT
mu.*,
m.title,
m.content,
m.message_type as message_type,
m.priority,
m.sender_name as sender_name,
m.sender_dept_name as sender_dept_name,
m.actual_send_time as actual_send_time
FROM tb_sys_message_user mu
INNER JOIN tb_sys_message m ON mu.message_id = m.message_id
WHERE mu.user_id = #{userID}
AND mu.message_id = #{messageID}
AND mu.deleted = 0
AND m.deleted = 0
</select>
<!-- 标记消息为已读 -->
<update id="markAsRead">
UPDATE tb_sys_message_user
SET is_read = 1,
send_status = 'sent',
read_time = NOW(),
update_time = NOW()
WHERE user_id = #{userID}
AND message_id = #{messageID}
AND deleted = 0
</update>
<!-- 批量标记消息为已读 -->
<update id="batchMarkAsRead">
UPDATE tb_sys_message_user
SET is_read = 1,
read_time = NOW(),
update_time = NOW()
WHERE user_id = #{userID}
AND message_id IN
<foreach collection="messageIDs" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
AND deleted = 0
</update>
<!-- 查询未读消息数量 -->
<select id="countUnread" resultType="java.lang.Integer">
SELECT COUNT(*)
FROM tb_sys_message_user
WHERE user_id = #{userID}
AND is_read = 0
AND deleted = 0
</select>
<!-- 动态计算未读消息数量(基于 target 配置) -->
<select id="countUnreadWithDynamicTargets" resultType="java.lang.Integer">
SELECT COUNT(DISTINCT m.message_id)
FROM tb_sys_message m
INNER JOIN tb_sys_message_target mt ON m.message_id = mt.message_id AND mt.deleted = 0
LEFT JOIN tb_sys_message_user mu ON m.message_id = mu.message_id AND mu.user_id = #{userID} AND mu.deleted = 0
WHERE m.deleted = 0
AND m.status IN ('sent', 'sending', 'failed')
AND COALESCE(mu.is_read, 0) = 0
AND (
-- 用户类型目标:直接匹配
(mt.target_type = 'user' AND mt.target_id = #{userID})
OR
-- 部门类型目标:用户所在部门是目标部门或其子部门
(mt.target_type = 'dept' AND EXISTS (
SELECT 1 FROM tb_sys_user_dept_role udr
INNER JOIN tb_sys_dept d ON udr.dept_id = d.dept_id AND d.deleted = 0
INNER JOIN tb_sys_dept target_dept ON target_dept.dept_id = mt.target_id AND target_dept.deleted = 0
WHERE udr.user_id = #{userID}
AND udr.deleted = 0
AND (
d.dept_id = target_dept.dept_id
OR d.dept_path LIKE CONCAT(target_dept.dept_path, '%')
)
))
OR
-- 角色类型目标:用户在指定部门范围内拥有该角色
(mt.target_type = 'role' AND EXISTS (
SELECT 1 FROM tb_sys_user_dept_role udr
INNER JOIN tb_sys_dept d ON udr.dept_id = d.dept_id AND d.deleted = 0
WHERE udr.user_id = #{userID}
AND udr.deleted = 0
AND udr.role_id = mt.target_id
AND d.dept_path LIKE CONCAT(
(SELECT dept_path FROM tb_sys_dept WHERE dept_id = mt.scope_dept_id AND deleted = 0),
'%'
)
))
)
</select>
<!-- 更新用户消息的发送状态 -->
<update id="updateSendStatus">
UPDATE tb_sys_message_user
SET send_status = #{sendStatus},
<if test="failReason != null and failReason != ''">
fail_reason = #{failReason},
</if>
update_time = NOW()
WHERE id = #{id}
AND deleted = 0
</update>
<!-- 查询待发送的用户消息列表 -->
<select id="selectPendingUserMessages" resultMap="BaseResultMap">
SELECT *
FROM tb_sys_message_user
WHERE deleted = 0
<if test="filter != null">
<if test="filter.messageID != null and filter.messageID != ''">
AND message_id = #{filter.messageID}
</if>
<if test="filter.sendStatus != null and filter.sendStatus != ''">
AND send_status = #{filter.sendStatus}
</if>
<if test="filter.deleted != null">
AND deleted = #{filter.deleted}
</if>
</if>
ORDER BY create_time ASC
</select>
<!-- 动态查询当前用户可见的消息列表(基于 target 配置计算) -->
<select id="selectMyMessagesWithDynamicTargets" resultMap="MessageUserVOMap">
SELECT
m.message_id,
m.title,
m.content,
m.message_type,
m.priority,
m.sender_name,
m.sender_dept_name,
m.actual_send_time,
MAX(COALESCE(mu.is_read, 0)) as is_read,
MAX(mu.read_time) as read_time,
MAX(COALESCE(mu.send_status, 'pending')) as send_status,
MAX(mu.id) as id,
MAX(mu.user_id) as user_id,
MAX(mu.send_method) as send_method,
MAX(mu.fail_reason) as fail_reason,
MAX(mu.create_time) as create_time
FROM tb_sys_message m
INNER JOIN tb_sys_message_target mt ON m.message_id = mt.message_id AND mt.deleted = 0
LEFT JOIN tb_sys_message_user mu ON m.message_id = mu.message_id AND mu.user_id = #{userID} AND mu.deleted = 0
WHERE m.deleted = 0
AND m.status IN ('sent', 'sending', 'failed')
AND (
-- 用户类型目标:直接匹配
(mt.target_type = 'user' AND mt.target_id = #{userID})
OR
-- 部门类型目标:用户所在部门是目标部门或其子部门
(mt.target_type = 'dept' AND EXISTS (
SELECT 1 FROM tb_sys_user_dept_role udr
INNER JOIN tb_sys_dept d ON udr.dept_id = d.dept_id AND d.deleted = 0
INNER JOIN tb_sys_dept target_dept ON target_dept.dept_id = mt.target_id AND target_dept.deleted = 0
WHERE udr.user_id = #{userID}
AND udr.deleted = 0
AND (
d.dept_id = target_dept.dept_id
OR d.dept_path LIKE CONCAT(target_dept.dept_path, '%')
)
))
OR
-- 角色类型目标:用户在指定部门范围内拥有该角色
(mt.target_type = 'role' AND EXISTS (
SELECT 1 FROM tb_sys_user_dept_role udr
INNER JOIN tb_sys_dept d ON udr.dept_id = d.dept_id AND d.deleted = 0
WHERE udr.user_id = #{userID}
AND udr.deleted = 0
AND udr.role_id = mt.target_id
AND d.dept_path LIKE CONCAT(
(SELECT dept_path FROM tb_sys_dept WHERE dept_id = mt.scope_dept_id AND deleted = 0),
'%'
)
))
)
<if test="filter != null">
<if test="filter.isRead != null">
AND COALESCE(mu.is_read, 0) = #{filter.isRead}
</if>
<if test="filter.sendMethod != null and filter.sendMethod != ''">
AND mt.send_method = #{filter.sendMethod}
</if>
<if test="filter.sendStatus != null and filter.sendStatus != ''">
AND mu.send_status = #{filter.sendStatus}
</if>
<if test="filter.priority != null and filter.priority != ''">
AND m.priority = #{filter.priority}
</if>
<if test="filter.messageType != null and filter.messageType != ''">
AND m.message_type = #{filter.messageType}
</if>
<if test="filter.title != null and filter.title != ''">
AND m.title LIKE CONCAT('%', #{filter.title}, '%')
</if>
</if>
GROUP BY m.message_id, m.title, m.content, m.message_type, m.priority,
m.sender_name, m.sender_dept_name, m.actual_send_time
ORDER BY m.actual_send_time DESC, m.create_time DESC
</select>
<!-- 查询或创建用户消息记录 -->
<select id="selectOrCreateUserMessage" resultMap="MessageUserVOMap">
SELECT
mu.*,
m.title,
m.content,
m.message_type,
m.priority,
m.sender_name,
m.sender_dept_name,
m.actual_send_time
FROM tb_sys_message m
LEFT JOIN tb_sys_message_user mu ON m.message_id = mu.message_id
AND mu.user_id = #{userID}
AND mu.deleted = 0
WHERE m.message_id = #{messageID}
AND m.deleted = 0
</select>
<!-- 插入用户消息记录(如果不存在) -->
<insert id="insertIfNotExists">
INSERT INTO tb_sys_message_user
(id, message_id, user_id, send_method, is_read, send_status, creator, create_time, update_time, deleted)
SELECT
#{userMessage.id},
#{userMessage.messageID},
#{userMessage.userID},
#{userMessage.sendMethod},
0,
'pending',
#{userMessage.creator},
NOW(),
NOW(),
0
FROM DUAL
WHERE NOT EXISTS (
SELECT 1 FROM tb_sys_message_user
WHERE message_id = #{userMessage.messageID}
AND user_id = #{userMessage.userID}
AND deleted = 0
)
</insert>
</mapper>