This commit is contained in:
jiangh277
2025-12-24 14:17:19 +08:00
parent 3eb445291f
commit 4c7d59f87b
89 changed files with 3525 additions and 311 deletions

View File

@@ -1,20 +1,45 @@
spring.application.name=timeline.user
server.port=30003
# ?????
spring.datasource.url=jdbc:mysql://8.137.148.196:33306/timeline?serverTimezone=UTC&allowPublicKeyRetrieval=true
# 数据库配置
spring.datasource.url=jdbc:mysql://59.80.22.43:33306/timeline?serverTimezone=UTC&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=your_password
spring.datasource.password=WoCloud@9ol7uj
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# MyBatis ??
# MyBatis 配置
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.timeline.user.entity
mybatis.configuration.mapUnderscoreToCamelCase=true
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
logging.level.com.timeline.user.dao=DEBUG
logging.level.org.mybatis=DEBUG
# JWT ??
jwt.secret=timelineSecretKey
jwt.expiration=86400
# JWT 配置
jwt.secret=6f3f9c2b9d9a4e3f8c0d6a7b5c4e3f1a6f3f9c2b9d9a4e3f8c0d6a7b5c4e3f1a
# Access Token 过期时间默认15分钟
jwt.access-expiration=900
# Refresh Token 过期时间默认7天
jwt.refresh-expiration=604800
# ????
# 日志配置
logging.level.com.timeline.user=DEBUG
# Redis (本地默认配置,按需调整)
spring.data.redis.host=127.0.0.1
spring.data.redis.port=36379
spring.data.redis.password=123456
spring.data.redis.timeout=5000
# 连接池
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=2
spring.datasource.hikari.idle-timeout=300000
# 5分钟低于 wait_timeout
spring.datasource.hikari.max-lifetime=1800000
# 30分钟低于 MySQL wait_timeout默认8小时
spring.datasource.hikari.connection-timeout=30000
# 获取连接超时
spring.datasource.hikari.keepalive-time=0
# 如 MySQL wait_timeout 较短,可设 180000(3分钟)
spring.datasource.hikari.validation-timeout=5000

View File

@@ -0,0 +1,41 @@
<?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.timeline.user.dao.FriendMapper">
<insert id="insert" parameterType="com.timeline.user.entity.Friendship">
INSERT INTO friendships (user_id, friend_id, sorted_key, status, create_time, update_time)
VALUES (#{userId}, #{friendId}, #{sortKey}, #{status}, #{createTime}, #{updateTime})
</insert>
<select id="selectByUsers" resultType="com.timeline.user.entity.Friendship">
SELECT * FROM friendships WHERE user_id = #{userId} AND friend_id = #{friendId}
</select>
<update id="updateStatus">
UPDATE friendships
SET status = #{status},
update_time = NOW()
WHERE user_id = #{userId} AND friend_id = #{friendId}
</update>
<select id="selectFriends" resultType="com.timeline.user.dto.FriendUserDto">
SELECT u.*, f.create_time AS createFriendTime, f.status AS friendStatus
FROM friendships f
JOIN user u ON f.friend_id = u.user_id
WHERE f.user_id = #{userId} AND f.status != 'rejected'
ORDER BY f.create_time DESC;
</select>
<select id="selectPending" resultType="com.timeline.user.entity.Friend">
SELECT * FROM user_friend
WHERE friend_id = #{toUserId} AND status = 0
</select>
<!-- <select id="selectFriendUsers" resultType="com.timeline.user.dto.FriendUser">
</select> -->
</mapper>

View File

@@ -0,0 +1,31 @@
<?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.timeline.user.dao.FriendNotifyMapper">
<insert id="insert" parameterType="com.timeline.user.entity.FriendNotify">
INSERT INTO friend_notify (from_user_id, to_user_id, type, status, content, create_time)
VALUES (#{fromUserId}, #{toUserId}, #{type}, #{status}, #{content}, #{createTime})
</insert>
<select id="selectUnread" resultType="com.timeline.user.entity.FriendNotify">
SELECT * FROM friend_notify
WHERE to_user_id = #{toUserId} AND status = 'unread'
ORDER BY create_time DESC
</select>
<select id="selectAllByUser" resultType="com.timeline.user.entity.FriendNotify">
SELECT * FROM friend_notify
WHERE to_user_id = #{toUserId}
ORDER BY create_time DESC
</select>
<update id="markRead">
UPDATE friend_notify
SET status = 'read', read_time = NOW()
WHERE id = #{id}
</update>
</mapper>

View File

@@ -20,14 +20,41 @@
<select id="selectByUsername" resultType="com.timeline.user.entity.User">
SELECT * FROM user WHERE username = #{username} AND is_deleted = 0
</select>
<select id="searchUsers" resultType="com.timeline.user.entity.User">
SELECT
u.user_id,
u.username,
u.avator,
u.description,
u.location,
u.email
FROM
`user` u
<where>
<if test="username != null and username != ''">
AND u.username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="nickname != null and nickname != ''">
AND u.nickname = #{nickname}
</if>
<if test="phone != null and phone != ''">
AND u.phone = #{phone}
</if>
<if test="email != null and email != ''">
AND u.email = #{email}
</if>
</where>
</select>
<update id="update" parameterType="com.timeline.user.entity.User">
UPDATE user
SET username = #{username},
email = #{email},
phone = #{phone},
status = #{status},
update_time = #{updateTime}
update_time = #{updateTime},
location = #{location},
description = #{description},
tag = #{tag}
WHERE user_id = #{userId} AND is_deleted = 0
</update>

View File

@@ -0,0 +1,6 @@
{
"entropy": 123456789,
"origins": ["*:*"],
"cookie_needed": true,
"websocket": true
}

View File

@@ -0,0 +1,132 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocket 测试页面</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.container {
border: 1px solid #ddd;
border-radius: 5px;
padding: 20px;
margin-bottom: 20px;
}
input, button {
padding: 8px;
margin: 5px;
}
#messages {
height: 300px;
overflow-y: scroll;
border: 1px solid #eee;
padding: 10px;
background-color: #f9f9f9;
}
.message {
padding: 5px;
border-bottom: 1px solid #eee;
}
</style>
</head>
<body>
<h1>WebSocket 消息推送测试</h1>
<div class="container">
<h2>连接设置</h2>
<label>用户ID: <input type="text" id="userId" placeholder="输入用户ID"></label>
<label>Token: <input type="text" id="token" placeholder="输入认证Token"></label>
<button onclick="connect()">连接</button>
<button onclick="disconnect()">断开连接</button>
<div id="connection-status">未连接</div>
</div>
<div class="container">
<h2>发送消息</h2>
<label>目标用户ID: <input type="text" id="targetUserId" placeholder="目标用户ID"></label><br>
<label>消息内容: <input type="text" id="messageContent" placeholder="消息内容"></label><br>
<label>目的地: <input type="text" id="destination" value="/queue/notification" placeholder="消息目的地"></label><br>
<button onclick="sendMessage()">发送消息</button>
</div>
<div class="container">
<h2>接收到的消息</h2>
<div id="messages"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/stompjs@2/lib/stomp.min.js"></script>
<script>
let stompClient = null;
function connect() {
const userId = document.getElementById('userId').value;
const token = document.getElementById('token').value;
if (!userId) {
alert('请输入用户ID');
return;
}
// 构造WebSocket连接URL包含token参数
const socket = new SockJS('http://127.0.0.1:8000/user-api/ws?Authorization=' + token);
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
document.getElementById('connection-status').innerText = '已连接';
// 订阅所有必要的消息队列
stompClient.subscribe('/user/queue/chat', function (message) {
showMessage('聊天消息: ' + message.body);
});
stompClient.subscribe('/user/queue/friend', function (message) {
showMessage('好友通知: ' + message.body);
});
stompClient.subscribe('/user/queue/notification', function (message) {
showMessage('系统通知: ' + message.body);
});
});
}
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
document.getElementById('connection-status').innerText = '未连接';
console.log("Disconnected");
}
function sendMessage() {
const targetUserId = document.getElementById('targetUserId').value;
const content = document.getElementById('messageContent').value;
const destination = document.getElementById('destination').value;
if (!targetUserId || !content) {
alert('请填写目标用户ID和消息内容');
return;
}
// 这里只是一个示例实际应该调用后端API来发送消息
showMessage('发送消息到用户 ' + targetUserId + ': ' + content);
}
function showMessage(message) {
const messagesDiv = document.getElementById('messages');
const messageElement = document.createElement('div');
messageElement.className = 'message';
messageElement.innerText = new Date().toLocaleTimeString() + ' - ' + message;
messagesDiv.appendChild(messageElement);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
</script>
</body>
</html>