fix: 代码审查问题修复 — socket 代理提供 + inject 空安全 + 观战人数响应式

This commit is contained in:
2026-06-23 11:28:27 +08:00
parent cf1a14bf24
commit f937b5cdb3
6 changed files with 54 additions and 21 deletions

View File

@@ -52,6 +52,9 @@ export const useRoomStore = defineStore('room', () => {
const drawRequested = ref<boolean>(false)
const drawRequestFrom = ref<string | null>(null)
// ===== 观战者 =====
const spectatorCount = ref<number>(0)
/**
* 从房间详情 HTTP 响应初始化 Store
* @param detail - 房间详情响应数据
@@ -129,6 +132,11 @@ export const useRoomStore = defineStore('room', () => {
resultType.value = type
}
/** 更新观战人数 */
function setSpectatorCount(count: number) {
spectatorCount.value = count
}
/** 重置状态 */
function reset() {
roomId.value = ''
@@ -150,8 +158,8 @@ export const useRoomStore = defineStore('room', () => {
boardState, currentTurn, moves, winner, resultType,
isPlaying, isFinished,
messages,
drawRequested, drawRequestFrom,
drawRequested, drawRequestFrom, spectatorCount,
initFromDetail, applyMove, setCurrentTurn, addMessage,
updateReady, addPlayer, removePlayer, setGameOver, reset
updateReady, addPlayer, removePlayer, setGameOver, setSpectatorCount, reset
}
})

View File

@@ -45,6 +45,28 @@ const accountStore = useAccountStore()
let socket: RoomSocket | null = null
/**
* WebSocket 代理对象
*
* 在 WS 未建立之前 provide 给子组件的安全代理。
* 子组件调用 send/close/isOpen 时,代理将请求转发到实际 socket。
* WS 未连接时 send() 静默丢弃消息,避免子组件因 inject undefined 而崩溃。
*/
const socketProxy: RoomSocket = {
send(type: string, data?: unknown) {
socket?.send(type, data)
},
close() {
socket?.close()
},
isOpen() {
return socket?.isOpen() ?? false
}
}
// 在 setup 阶段同步 provide确保子组件不会 inject 到 undefined
provide('roomSocket', socketProxy)
onMounted(async () => {
const roomId = route.params.roomId as string
const userId = accountStore.accountInfo?.userId ?? ''
@@ -64,8 +86,7 @@ onMounted(async () => {
}
})
// 3. 向子组件暴露 WebSocket 控制接口
provide('roomSocket', socket)
// socketProxy 已在 setup 阶段同步 provide子组件现在调用 send() 即可
} catch {
// 房间不存在或加载失败,返回大厅
router.push('/')
@@ -119,6 +140,16 @@ function handleWsMessage(msg: WsMessage) {
roomStore.updateReady(data.userId, data.ready)
break
}
case 'spectator_join': {
const data = msg.data as { count: number }
roomStore.setSpectatorCount(data.count)
break
}
case 'spectator_leave': {
const data = msg.data as { count: number }
roomStore.setSpectatorCount(data.count)
break
}
}
}
</script>

View File

@@ -29,7 +29,7 @@ const canvasSize = PADDING * 2 + CELL_SIZE * (BOARD_SIZE - 1)
const store = useRoomStore()
const { boardState, currentTurn, currentUserId, isPlayer, isPlaying } = storeToRefs(store)
const socket = inject<RoomSocket>('roomSocket')!
const socket = inject<RoomSocket>('roomSocket', null)!
const canvasRef = ref<HTMLCanvasElement | null>(null)
const hoverPos = ref<{ row: number; col: number } | null>(null)

View File

@@ -47,7 +47,7 @@ const { players, currentUserId } = storeToRefs(store)
const router = useRouter()
/** 通过 inject 获取 WS 连接(父组件 provide */
const socket = inject<RoomSocket>('roomSocket')!
const socket = inject<RoomSocket>('roomSocket', null)!
/** 当前用户是否已准备 */
const isReady = computed(() => {

View File

@@ -33,7 +33,12 @@ import type { RoomSocket } from '@/common/websocket/roomSocket'
const store = useRoomStore()
const { messages, players } = storeToRefs(store)
const socket = inject<RoomSocket>('roomSocket')!
const socket = inject<RoomSocket>('roomSocket', null)
/** 发送消息空安全socket 尚未建立时静默丢弃) */
function safeSend(type: string, data?: unknown) {
socket?.send(type, data)
}
/** 输入框内容 */
const input = ref('')
@@ -54,7 +59,7 @@ watch(() => messages.value.length, async () => {
function sendMessage() {
const content = input.value.trim()
if (!content) return
socket.send('chat', { content })
safeSend('chat', { content })
input.value = ''
}

View File

@@ -8,22 +8,11 @@
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { storeToRefs } from 'pinia'
import { useRoomStore } from '@/stores'
import { storeToRefs } from 'pinia'
const store = useRoomStore()
const { players } = storeToRefs(store)
/**
* 观战人数估算WS 连接数 - 玩家数
* 后续由 WS spectator_join/leave 事件维护更准确的数据
*/
const spectatorCount = computed(() => {
// 首期简化处理:后端 spectator 数据通过 WS 事件维护
// 这里暂时返回固定值,后续通过 Store 中的 spectators 列表计算
return 0
})
const { spectatorCount } = storeToRefs(store)
</script>
<style scoped lang="scss">