Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
越 贾
JYChatRoom1.0
Commits
4eab09ff
Commit
4eab09ff
authored
Aug 28, 2025
by
越 贾
Browse files
ver2: 新增 room/message/getUpdate 接口,将消息轮询优化为只轮询最新消息;登录用户每次进入聊天室默认打开最近访问的房间
parent
bac5d6b8
Changes
4
Hide whitespace changes
Inline
Side-by-side
chatroom/prisma/dev.db
View file @
4eab09ff
No preview for this file type
chatroom/src/app/api/room/message/getUpdate/route.ts
0 → 100644
View file @
4eab09ff
// src/app/api/room/message/getUpdate/route.ts
import
{
NextRequest
,
NextResponse
}
from
'
next/server
'
;
import
{
PrismaClient
}
from
'
@prisma/client
'
;
const
prisma
=
new
PrismaClient
();
export
async
function
GET
(
req
:
NextRequest
)
{
try
{
const
{
searchParams
}
=
new
URL
(
req
.
url
);
const
roomId
=
searchParams
.
get
(
'
roomId
'
);
const
sinceMessageId
=
searchParams
.
get
(
'
sinceMessageId
'
);
if
(
!
roomId
)
{
return
NextResponse
.
json
({
code
:
1
,
message
:
'
缺少 roomId 参数
'
},
{
status
:
400
});
}
if
(
!
sinceMessageId
)
{
return
NextResponse
.
json
({
code
:
1
,
message
:
'
缺少 sinceMessageId 参数
'
},
{
status
:
400
});
}
// 获取指定消息ID之后的所有新消息
const
messages
=
await
prisma
.
message
.
findMany
({
where
:
{
roomId
:
Number
(
roomId
),
id
:
{
gt
:
Number
(
sinceMessageId
)
// greater than - 大于指定的消息ID
}
},
orderBy
:
{
createdAt
:
'
asc
'
},
});
const
formattedMessages
=
messages
.
map
(
msg
=>
({
messageId
:
msg
.
id
,
roomId
:
msg
.
roomId
,
sender
:
msg
.
sender
,
content
:
msg
.
content
,
time
:
msg
.
createdAt
.
getTime
(),
}));
return
NextResponse
.
json
({
code
:
0
,
data
:
{
messages
:
formattedMessages
}
});
}
catch
(
error
)
{
console
.
error
(
'
获取增量消息失败:
'
,
error
);
return
NextResponse
.
json
({
code
:
1
,
message
:
'
获取消息失败
'
},
{
status
:
500
});
}
}
\ No newline at end of file
chatroom/src/app/chat/page.tsx
View file @
4eab09ff
...
...
@@ -73,7 +73,6 @@ function MessageItem({ message, currentUser }: MessageItemProps) {
);
}
// 单个房间组件
// 登录状态组件
function
AuthStatus
({
isLoggedIn
,
username
,
onLogout
}:
{
isLoggedIn
:
boolean
;
...
...
@@ -124,13 +123,13 @@ function PermissionAlert({ message, onClose }: { message: string; onClose: () =>
);
}
// 单个房间组件
- 添加权限检查
// 单个房间组件
interface
RoomEntryProps
{
room
:
RoomPreviewInfo
;
onClick
:
()
=>
void
;
isActive
:
boolean
;
onDelete
:
()
=>
void
;
canDelete
:
boolean
;
// 新增:是否可以删除
canDelete
:
boolean
;
}
function
RoomEntry
({
room
,
onClick
,
isActive
,
onDelete
,
canDelete
}:
RoomEntryProps
)
{
...
...
@@ -178,9 +177,35 @@ export default function ChatPage() {
const
[
permissionAlertMessage
,
setPermissionAlertMessage
]
=
useState
(
''
);
const
router
=
useRouter
();
// 新增:消息相关状态
const
[
messages
,
setMessages
]
=
useState
<
Message
[]
>
([]);
const
[
lastMessageId
,
setLastMessageId
]
=
useState
<
number
>
(
0
);
const
[
isFirstLoad
,
setIsFirstLoad
]
=
useState
(
true
);
// 使用身份验证 hook
const
{
isLoggedIn
,
username
:
authUsername
,
isLoading
:
authLoading
,
logout
}
=
useAuth
();
// v2新增:保存最后访问的房间ID到 localStorage
const
saveLastRoom
=
(
roomId
:
number
|
null
)
=>
{
if
(
roomId
)
{
localStorage
.
setItem
(
'
lastRoomId
'
,
roomId
.
toString
());
}
else
{
localStorage
.
removeItem
(
'
lastRoomId
'
);
}
};
// v2新增读取最后访问的房间ID
const
getLastRoom
=
():
number
|
null
=>
{
const
lastRoom
=
localStorage
.
getItem
(
'
lastRoomId
'
);
return
lastRoom
?
parseInt
(
lastRoom
,
10
)
:
null
;
};
// v2新增更新 selectedRoomId 的设置函数,同时保存到 localStorage
const
handleSelectRoom
=
(
roomId
:
number
|
null
)
=>
{
setSelectedRoomId
(
roomId
);
saveLastRoom
(
roomId
);
};
useEffect
(()
=>
{
const
saved
=
localStorage
.
getItem
(
'
nickname
'
);
if
(
!
saved
)
{
...
...
@@ -213,18 +238,80 @@ export default function ChatPage() {
{
refreshInterval
:
1000
}
);
// 获取消息列表
// v2新增:房间列表加载完成后,自动选择上次访问的房间
useEffect
(()
=>
{
// 只在房间列表首次加载完成且没有选中房间时执行
if
(
roomData
?.
rooms
&&
!
selectedRoomId
)
{
const
lastRoomId
=
getLastRoom
();
if
(
lastRoomId
)
{
// 检查上次的房间是否仍然存在
const
roomExists
=
roomData
.
rooms
.
some
(
r
=>
r
.
roomId
===
lastRoomId
);
if
(
roomExists
)
{
console
.
log
(
`自动进入上次访问的房间:
${
lastRoomId
}
`
);
setSelectedRoomId
(
lastRoomId
);
}
else
{
// 如果上次的房间不存在了,清除记录
console
.
log
(
'
上次访问的房间已不存在
'
);
localStorage
.
removeItem
(
'
lastRoomId
'
);
}
}
}
},
[
roomData
,
selectedRoomId
]);
// v2改进:首次加载获取全部消息
const
{
data
:
messageData
,
error
:
messageError
,
isLoading
:
messageIsLoading
,
mutate
:
refreshMessages
,
data
:
initialMessages
,
error
:
initialError
,
isLoading
:
initialLoading
}
=
useSWR
<
RoomMessageListRes
>
(
selectedRoomId
?
`/api/room/message/list?roomId=
${
selectedRoomId
}
`
:
null
,
selectedRoomId
&&
isFirstLoad
?
`/api/room/message/list?roomId=
${
selectedRoomId
}
`
:
null
,
(
key
)
=>
authFetch
(
key
).
then
((
r
)
=>
r
.
json
().
then
((
d
:
any
)
=>
d
.
data
)),
{
revalidateOnFocus
:
false
,
revalidateOnReconnect
:
false
}
);
// v2改进:增量获取新消息
const
{
data
:
newMessages
}
=
useSWR
<
RoomMessageListRes
>
(
selectedRoomId
&&
lastMessageId
>
0
?
`/api/room/message/getUpdate?roomId=
${
selectedRoomId
}
&sinceMessageId=
${
lastMessageId
}
`
:
null
,
(
key
)
=>
authFetch
(
key
).
then
((
r
)
=>
r
.
json
().
then
((
d
:
any
)
=>
d
.
data
)),
{
refreshInterval
:
1000
}
);
// v2处理首次加载的消息
useEffect
(()
=>
{
if
(
initialMessages
?.
messages
&&
isFirstLoad
)
{
setMessages
(
initialMessages
.
messages
);
if
(
initialMessages
.
messages
.
length
>
0
)
{
const
lastMsg
=
initialMessages
.
messages
[
initialMessages
.
messages
.
length
-
1
];
setLastMessageId
(
lastMsg
.
messageId
);
}
setIsFirstLoad
(
false
);
}
},
[
initialMessages
,
isFirstLoad
]);
// v2处理增量更新的消息
useEffect
(()
=>
{
if
(
newMessages
?.
messages
&&
newMessages
.
messages
.
length
>
0
)
{
setMessages
(
prev
=>
[...
prev
,
...
newMessages
.
messages
]);
const
lastMsg
=
newMessages
.
messages
[
newMessages
.
messages
.
length
-
1
];
setLastMessageId
(
lastMsg
.
messageId
);
}
},
[
newMessages
]);
// v2切换房间时重置状态
useEffect
(()
=>
{
setMessages
([]);
setLastMessageId
(
0
);
setIsFirstLoad
(
true
);
},
[
selectedRoomId
]);
// 创建房间
const
{
trigger
:
addRoomTrigger
,
isMutating
:
isAddingRoom
}
=
useSWRMutation
<
RoomAddRes
,
...
...
@@ -240,15 +327,15 @@ export default function ChatPage() {
// 删除房间
const
{
trigger
:
deleteRoomTrigger
}
=
useSWRMutation
(
'
/api/room/delete
'
,
// 确保路径正确
async
(
key
,
{
arg
}:
{
arg
:
{
roomId
:
number
}
})
=>
{
const
response
=
await
authFetch
(
key
,
{
method
:
'
POST
'
,
body
:
JSON
.
stringify
({
roomId
:
arg
.
roomId
}),
// 参数格式修正
});
return
response
;
}
);
'
/api/room/delete
'
,
async
(
key
,
{
arg
}:
{
arg
:
{
roomId
:
number
}
})
=>
{
const
response
=
await
authFetch
(
key
,
{
method
:
'
POST
'
,
body
:
JSON
.
stringify
({
roomId
:
arg
.
roomId
}),
});
return
response
;
}
);
// 发送消息
const
{
trigger
:
sendMessageTrigger
,
isMutating
:
isSendingMessage
}
=
...
...
@@ -279,7 +366,9 @@ export default function ChatPage() {
try
{
const
res
=
await
addRoomTrigger
({
user
:
nickname
,
roomName
:
newRoomName
.
trim
()
});
refreshRooms
();
if
(
res
?.
roomId
)
setSelectedRoomId
(
res
.
roomId
);
if
(
res
?.
roomId
)
{
handleSelectRoom
(
res
.
roomId
);
// 🆕 使用新的设置函数
}
setShowAddModal
(
false
);
setNewRoomName
(
''
);
}
catch
(
error
)
{
...
...
@@ -290,50 +379,46 @@ export default function ChatPage() {
// 处理删除房间
const
handleDeleteRoom
=
async
(
roomId
:
number
)
=>
{
if
(
!
isLoggedIn
)
{
alert
(
'
请先登录后再删除房间
'
);
return
;
}
const
token
=
localStorage
.
getItem
(
'
token
'
);
if
(
!
token
)
{
alert
(
'
登录状态已过期,请重新登录
'
);
logout
();
return
;
}
if
(
!
isLoggedIn
)
{
alert
(
'
请先登录后再删除房间
'
);
return
;
}
if
(
!
confirm
(
'
确定删除该房间?此操作不可撤销!
'
))
return
;
try
{
await
deleteRoomTrigger
({
roomId
});
// 删除成功后的处理
if
(
selectedRoomId
===
roomId
)
{
setSelectedRoomId
(
null
);
const
token
=
localStorage
.
getItem
(
'
token
'
);
if
(
!
token
)
{
alert
(
'
登录状态已过期,请重新登录
'
);
logout
();
return
;
}
if
(
!
confirm
(
'
确定删除该房间?此操作不可撤销!
'
))
return
;
// 立即刷新房间列表
refreshRooms
();
alert
(
'
房间删除成功
'
);
}
catch
(
error
)
{
console
.
error
(
'
删除房间失败:
'
,
error
);
if
(
error
instanceof
Error
)
{
if
(
error
.
message
.
includes
(
'
认证
'
)
||
error
.
message
.
includes
(
'
token
'
))
{
alert
(
'
认证失败,请重新登录
'
);
logout
();
try
{
await
deleteRoomTrigger
({
roomId
});
if
(
selectedRoomId
===
roomId
)
{
handleSelectRoom
(
null
);
// 🆕 使用新的设置函数
}
refreshRooms
();
alert
(
'
房间删除成功
'
);
}
catch
(
error
)
{
console
.
error
(
'
删除房间失败:
'
,
error
);
if
(
error
instanceof
Error
)
{
if
(
error
.
message
.
includes
(
'
认证
'
)
||
error
.
message
.
includes
(
'
token
'
))
{
alert
(
'
认证失败,请重新登录
'
);
logout
();
}
else
{
alert
(
`删除房间失败:
${
error
.
message
}
`
);
}
}
else
{
alert
(
`
删除房间失败
:
${
error
.
message
}
`
);
alert
(
'
删除房间失败
,请稍后重试
'
);
}
}
else
{
alert
(
'
删除房间失败,请稍后重试
'
);
}
}
};
};
//
处理发送消息
//处理发送消息
const
handleSendMessage
=
async
()
=>
{
if
(
!
messageInput
.
trim
()
||
!
selectedRoomId
)
return
;
try
{
...
...
@@ -343,7 +428,8 @@ export default function ChatPage() {
sender
:
nickname
,
});
setMessageInput
(
''
);
refreshMessages
();
// 发送成功后,增量更新会自动获取新消息
// 不需要手动刷新
}
catch
{
alert
(
'
发送消息失败
'
);
}
...
...
@@ -360,6 +446,10 @@ export default function ChatPage() {
return
<
div
className
=
{
styles
.
loading
}
>
正在检查登录状态...
</
div
>;
}
//计算加载和错误状态
const
messageIsLoading
=
initialLoading
&&
isFirstLoad
;
const
messageError
=
initialError
;
return
(
<
main
className
=
{
styles
.
chatContainer
}
>
{
/* 身份验证状态显示 */
}
...
...
@@ -369,7 +459,7 @@ export default function ChatPage() {
onLogout
=
{
logout
}
/>
{
/* 左侧房间列表 */
}
{
/* 左侧房间列表
- 修改点击事件
*/
}
<
div
className
=
{
styles
.
sidebar
}
>
<
div
className
=
{
styles
.
sidebarHeader
}
>
<
h2
>
聊天室
</
h2
>
...
...
@@ -389,10 +479,10 @@ export default function ChatPage() {
<
RoomEntry
key
=
{
r
.
roomId
}
room
=
{
r
}
onClick
=
{
()
=>
set
Select
ed
Room
Id
(
r
.
roomId
)
}
onClick
=
{
()
=>
handle
SelectRoom
(
r
.
roomId
)
}
// 🆕 使用新的设置函数
isActive
=
{
selectedRoomId
===
r
.
roomId
}
onDelete
=
{
()
=>
handleDeleteRoom
(
r
.
roomId
)
}
canDelete
=
{
isLoggedIn
}
// 只有登录用户可以删除
canDelete
=
{
isLoggedIn
}
/>
))
}
</
div
>
...
...
@@ -411,7 +501,7 @@ export default function ChatPage() {
<
div
className
=
{
styles
.
messagesContainer
}
>
{
messageIsLoading
&&
<
div
className
=
{
styles
.
loading
}
>
加载消息中...
</
div
>
}
{
messageError
&&
<
div
className
=
{
styles
.
error
}
>
加载消息失败
</
div
>
}
{
message
Data
?.
messages
?
.
map
((
m
)
=>
(
{
message
s
.
map
((
m
)
=>
(
<
MessageItem
key
=
{
m
.
messageId
}
message
=
{
m
}
currentUser
=
{
nickname
}
/>
))
}
</
div
>
...
...
@@ -476,4 +566,4 @@ export default function ChatPage() {
)
}
</
main
>
);
}
}
\ No newline at end of file
chatroom/src/app/greet/greet.css
View file @
4eab09ff
div
{
/* chatroom/src/app/greet/greet.css */
.div
{
color
:
#c3aae2
;
text-align
:
center
;
padding
:
20px
;
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment