|
|
@@ -8,17 +8,13 @@ import type {
|
|
|
ExamSocketRoomMessage,
|
|
|
Answer
|
|
|
} from '../types.ts';
|
|
|
+import { useAuth } from "../../../hooks.tsx";
|
|
|
|
|
|
interface LoaderData {
|
|
|
token: string;
|
|
|
serverUrl: string;
|
|
|
}
|
|
|
|
|
|
-// 系统消息默认值
|
|
|
-const SYSTEM_USER_ID = 'system';
|
|
|
-const DEFAULT_HOLDING_STOCK = '0';
|
|
|
-const DEFAULT_HOLDING_CASH = '1';
|
|
|
-
|
|
|
// 工具函数:统一错误处理
|
|
|
const handleAsyncOperation = async <T>(
|
|
|
operation: () => Promise<T>,
|
|
|
@@ -40,8 +36,8 @@ interface ProfitResult {
|
|
|
|
|
|
function calculateProfit(currentPrice: number, previousPrice: number, holdingStock: string): ProfitResult {
|
|
|
if (holdingStock === '1') {
|
|
|
- const profitAmount = currentPrice - previousPrice;
|
|
|
- const profitPercent = ((currentPrice - previousPrice) / previousPrice) * 100;
|
|
|
+ const profitAmount = currentPrice - previousPrice; // 金额收益
|
|
|
+ const profitPercent = ((currentPrice - previousPrice) / previousPrice) * 100; // 百分比收益
|
|
|
return {
|
|
|
profitAmount,
|
|
|
profitPercent
|
|
|
@@ -53,12 +49,10 @@ function calculateProfit(currentPrice: number, previousPrice: number, holdingSto
|
|
|
};
|
|
|
}
|
|
|
|
|
|
-// 核心Socket客户端Hook
|
|
|
-export function useExamSocketClient(roomId: string | null) {
|
|
|
- const { token, serverUrl } = {
|
|
|
- token: '',
|
|
|
- serverUrl: '/wss'
|
|
|
- };
|
|
|
+// 使用react-query管理socket客户端
|
|
|
+export function useSocketClient(roomId: string | null) {
|
|
|
+ const { token } = useAuth();
|
|
|
+ const serverUrl = '/';
|
|
|
|
|
|
const { data: client } = useQuery({
|
|
|
queryKey: ['socket-client'],
|
|
|
@@ -84,52 +78,31 @@ export function useExamSocketClient(roomId: string | null) {
|
|
|
});
|
|
|
|
|
|
const joinRoom = useCallback(async (roomId: string) => {
|
|
|
- if (client && roomId) {
|
|
|
+ if (client) {
|
|
|
await client.socket.joinRoom(roomId);
|
|
|
}
|
|
|
}, [client]);
|
|
|
|
|
|
const leaveRoom = useCallback(async (roomId: string) => {
|
|
|
- if (client && roomId) {
|
|
|
+ if (client) {
|
|
|
await client.socket.leaveRoom(roomId);
|
|
|
}
|
|
|
}, [client]);
|
|
|
|
|
|
- const sendRoomMessage = useCallback(async (roomId: string | null, message: ExamSocketMessage) => {
|
|
|
- if (client && roomId) {
|
|
|
- const convertedMessage = {
|
|
|
- ...message,
|
|
|
- timestamp: Date.now(),
|
|
|
- content: {
|
|
|
- ...message.content,
|
|
|
- userId: message.content.userId || SYSTEM_USER_ID
|
|
|
- }
|
|
|
- };
|
|
|
- await client.socket.sendRoomMessage(roomId, convertedMessage as any);
|
|
|
+ const sendRoomMessage = useCallback(async (roomId: string, message: ExamSocketMessage) => {
|
|
|
+ if (client) {
|
|
|
+ await client.socket.sendRoomMessage(roomId, message as any);
|
|
|
}
|
|
|
}, [client]);
|
|
|
|
|
|
const onRoomMessage = useCallback((callback: (data: ExamSocketRoomMessage) => void) => {
|
|
|
if (client) {
|
|
|
- client.socket.onRoomMessage((data: any) => {
|
|
|
- const convertedMessage: ExamSocketRoomMessage = {
|
|
|
- roomId: data.roomId,
|
|
|
- message: {
|
|
|
- ...data.message,
|
|
|
- timestamp: Number(data.message.timestamp),
|
|
|
- content: {
|
|
|
- ...data.message.content,
|
|
|
- price: parseFloat(String(data.message.content.price))
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
- callback(convertedMessage);
|
|
|
- });
|
|
|
+ client.socket.onRoomMessage(callback);
|
|
|
}
|
|
|
}, [client]);
|
|
|
|
|
|
- const getAnswers = useCallback(async (roomId: string | null, questionId: string): Promise<Answer[]> => {
|
|
|
- if (!client || !roomId) return [];
|
|
|
+ const getAnswers = useCallback(async (roomId: string, questionId: string): Promise<Answer[]> => {
|
|
|
+ if (!client) return [];
|
|
|
|
|
|
return handleAsyncOperation(async () => {
|
|
|
const answersData = await client.redis.hgetall(`quiz:${roomId}:answers:${questionId}`);
|
|
|
@@ -142,11 +115,13 @@ export function useExamSocketClient(roomId: string | null) {
|
|
|
}, '获取答案失败');
|
|
|
}, [client]);
|
|
|
|
|
|
- const storeAnswer = useCallback(async (roomId: string | null, questionId: string, userId: string, answer: QuizContent) => {
|
|
|
- if (!client || !roomId) return;
|
|
|
+ const storeAnswer = useCallback(async (roomId: string, questionId: string, userId: string, answer: QuizContent) => {
|
|
|
+ if (!client) return;
|
|
|
|
|
|
+ // 获取历史价格数据
|
|
|
const pricesData = await client.redis.hgetall(`quiz:${roomId}:prices`);
|
|
|
if (!pricesData) {
|
|
|
+ // 如果没有历史数据,存储初始答案
|
|
|
const initialAnswer: Answer = {
|
|
|
...answer,
|
|
|
userId,
|
|
|
@@ -164,11 +139,13 @@ export function useExamSocketClient(roomId: string | null) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // 获取该用户的所有历史答案
|
|
|
const dates = Object.keys(pricesData).sort();
|
|
|
const allAnswers = await Promise.all(
|
|
|
dates.map(date => getAnswers(roomId, date))
|
|
|
);
|
|
|
|
|
|
+ // 过滤出当前用户的答案并添加价格信息
|
|
|
const userAnswers = allAnswers
|
|
|
.flat()
|
|
|
.filter((a: Answer) => a.userId === userId)
|
|
|
@@ -182,6 +159,7 @@ export function useExamSocketClient(roomId: string | null) {
|
|
|
})
|
|
|
.sort((a: Answer, b: Answer) => new Date(a.date || '').getTime() - new Date(b.date || '').getTime());
|
|
|
|
|
|
+ // 计算收益
|
|
|
let totalProfitAmount = 0;
|
|
|
let totalProfitPercent = 0;
|
|
|
|
|
|
@@ -197,6 +175,7 @@ export function useExamSocketClient(roomId: string | null) {
|
|
|
totalProfitPercent = (prevAnswer.totalProfitPercent || 0) + profitPercent;
|
|
|
}
|
|
|
|
|
|
+ // 存储带有收益信息的答案
|
|
|
const answerWithProfit: Answer = {
|
|
|
...answer,
|
|
|
userId,
|
|
|
@@ -215,8 +194,8 @@ export function useExamSocketClient(roomId: string | null) {
|
|
|
}
|
|
|
}, [client, getAnswers]);
|
|
|
|
|
|
- const cleanupRoom = useCallback(async (roomId: string | null, questionId?: string) => {
|
|
|
- if (!client || !roomId) return;
|
|
|
+ const cleanupRoom = useCallback(async (roomId: string, questionId?: string) => {
|
|
|
+ if (!client) return;
|
|
|
|
|
|
await handleAsyncOperation(async () => {
|
|
|
if (questionId) {
|
|
|
@@ -230,35 +209,37 @@ export function useExamSocketClient(roomId: string | null) {
|
|
|
}, '清理房间数据失败');
|
|
|
}, [client]);
|
|
|
|
|
|
- const sendNextQuestion = useCallback(async (roomId: string | null, state: QuizState) => {
|
|
|
- if (!client || !roomId) return;
|
|
|
+ const sendNextQuestion = useCallback(async (roomId: string, state: QuizState) => {
|
|
|
+ if (!client) return;
|
|
|
|
|
|
return handleAsyncOperation(async () => {
|
|
|
- const messageContent: QuizContent = {
|
|
|
- date: state.date,
|
|
|
- price: state.price,
|
|
|
- holdingStock: DEFAULT_HOLDING_STOCK,
|
|
|
- holdingCash: DEFAULT_HOLDING_CASH,
|
|
|
- userId: SYSTEM_USER_ID
|
|
|
- };
|
|
|
-
|
|
|
- const message: ExamSocketMessage = {
|
|
|
+ const message = {
|
|
|
type: 'question',
|
|
|
- content: messageContent
|
|
|
+ content: {
|
|
|
+ date: state.date,
|
|
|
+ price: state.price
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
- await storeAnswer(roomId, 'current_state', SYSTEM_USER_ID, messageContent);
|
|
|
+ // 存储当前问题状态
|
|
|
+ await storeAnswer(roomId, 'current_state', 'system', {
|
|
|
+ date: state.date,
|
|
|
+ price: state.price
|
|
|
+ });
|
|
|
+
|
|
|
+ // 存储价格历史记录
|
|
|
await client.redis.hset(
|
|
|
`quiz:${roomId}:prices`,
|
|
|
state.date,
|
|
|
JSON.stringify({ price: state.price })
|
|
|
);
|
|
|
+
|
|
|
await sendRoomMessage(roomId, message);
|
|
|
}, '发送题目失败');
|
|
|
}, [client, sendRoomMessage, storeAnswer]);
|
|
|
|
|
|
- const getCurrentQuestion = useCallback(async (roomId: string | null): Promise<QuizState | null> => {
|
|
|
- if (!client || !roomId) return null;
|
|
|
+ const getCurrentQuestion = useCallback(async (roomId: string): Promise<QuizState | null> => {
|
|
|
+ if (!client) return null;
|
|
|
|
|
|
return handleAsyncOperation(async () => {
|
|
|
const answers = await getAnswers(roomId, 'current_state');
|
|
|
@@ -273,8 +254,9 @@ export function useExamSocketClient(roomId: string | null) {
|
|
|
}, '获取当前题目状态失败');
|
|
|
}, [client, getAnswers]);
|
|
|
|
|
|
- const getPriceHistory = useCallback(async (roomId: string | null, date: string): Promise<string> => {
|
|
|
- if (!client || !roomId) return '0';
|
|
|
+ // 添加获取历史价格的函数
|
|
|
+ const getPriceHistory = useCallback(async (roomId: string, date: string): Promise<string> => {
|
|
|
+ if (!client) return '0';
|
|
|
|
|
|
return handleAsyncOperation(async () => {
|
|
|
const priceData = await client.redis.hget(`quiz:${roomId}:prices`, date);
|
|
|
@@ -301,8 +283,8 @@ export function useExamSocketClient(roomId: string | null) {
|
|
|
}
|
|
|
|
|
|
// Socket Room Hook
|
|
|
-export function useExamSocketRoom(roomId: string | null) {
|
|
|
- const socketClient = useExamSocketClient(roomId);
|
|
|
+export function useSocketRoom(roomId: string | null) {
|
|
|
+ const socketClient = useSocketClient(roomId);
|
|
|
|
|
|
const { data: roomConnection } = useQuery({
|
|
|
queryKey: ['socket-room', roomId],
|
|
|
@@ -324,9 +306,9 @@ export function useExamSocketRoom(roomId: string | null) {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
-// 当前题目状态Hook
|
|
|
-export function useExamCurrentQuestion(roomId: string | null) {
|
|
|
- const socketClient = useExamSocketClient(roomId);
|
|
|
+// 使用react-query管理当前题目状态
|
|
|
+export function useCurrentQuestion(roomId: string | null) {
|
|
|
+ const socketClient = useSocketClient(roomId);
|
|
|
|
|
|
const { data: currentQuestion, refetch } = useQuery({
|
|
|
queryKey: ['current-question', roomId],
|
|
|
@@ -344,9 +326,9 @@ export function useExamCurrentQuestion(roomId: string | null) {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
-// 房间消息Hook
|
|
|
-export function useExamRoomMessages(roomId: string | null) {
|
|
|
- const socketClient = useExamSocketClient(roomId);
|
|
|
+// 使用react-query管理房间消息
|
|
|
+export function useRoomMessages(roomId: string | null) {
|
|
|
+ const socketClient = useSocketClient(roomId);
|
|
|
const queryClient = useQueryClient();
|
|
|
const [lastMessage, setLastMessage] = useState<ExamSocketRoomMessage | null>(null);
|
|
|
|
|
|
@@ -357,6 +339,7 @@ export function useExamRoomMessages(roomId: string | null) {
|
|
|
setLastMessage(data);
|
|
|
const { type, content } = data.message;
|
|
|
|
|
|
+ // 处理不同类型的消息
|
|
|
switch (type) {
|
|
|
case 'question':
|
|
|
queryClient.invalidateQueries({ queryKey: ['current-question', roomId] });
|
|
|
@@ -367,15 +350,18 @@ export function useExamRoomMessages(roomId: string | null) {
|
|
|
break;
|
|
|
case 'settlement':
|
|
|
case 'submit':
|
|
|
+ // 刷新所有用户的答题历史
|
|
|
queryClient.invalidateQueries({
|
|
|
queryKey: ['user-answers'],
|
|
|
predicate: (query) => query.queryKey[1] === roomId
|
|
|
});
|
|
|
+ // 刷新当前答案缓存
|
|
|
queryClient.invalidateQueries({
|
|
|
queryKey: ['answers', roomId]
|
|
|
});
|
|
|
break;
|
|
|
case 'restart':
|
|
|
+ // 重置所有相关查询
|
|
|
queryClient.invalidateQueries({ queryKey: ['current-question', roomId] });
|
|
|
queryClient.invalidateQueries({ queryKey: ['answers', roomId] });
|
|
|
queryClient.invalidateQueries({
|
|
|
@@ -388,30 +374,58 @@ export function useExamRoomMessages(roomId: string | null) {
|
|
|
};
|
|
|
|
|
|
socketClient.onRoomMessage(handleMessage);
|
|
|
- return () => {
|
|
|
- socketClient.client?.socket.offRoomMessage(handleMessage);
|
|
|
- };
|
|
|
}, [roomId, socketClient, queryClient]);
|
|
|
|
|
|
return lastMessage;
|
|
|
}
|
|
|
|
|
|
-// 用户答案历史Hook
|
|
|
-export function useExamUserAnswerHistory(roomId: string | null, userId: string | null) {
|
|
|
- const socketClient = useExamSocketClient(roomId);
|
|
|
+// 使用react-query管理答案缓存
|
|
|
+export function useAnswerCache(roomId: string | null, date: string | null) {
|
|
|
+ const socketClient = useSocketClient(roomId);
|
|
|
+
|
|
|
+ const { data: answers, refetch: refetchAnswers } = useQuery({
|
|
|
+ queryKey: ['answers', roomId, date],
|
|
|
+ queryFn: async () => {
|
|
|
+ if (!roomId || !date || !socketClient) return [];
|
|
|
+ const answers = await socketClient.getAnswers(roomId, date);
|
|
|
+ const priceData = await socketClient.client?.redis.hget(`quiz:${roomId}:prices`, date);
|
|
|
+ if (!priceData) return answers;
|
|
|
+
|
|
|
+ const { price } = JSON.parse(priceData);
|
|
|
+ return answers.map((answer: Answer) => ({
|
|
|
+ ...answer,
|
|
|
+ price
|
|
|
+ }));
|
|
|
+ },
|
|
|
+ enabled: !!roomId && !!date && !!socketClient,
|
|
|
+ staleTime: 0
|
|
|
+ });
|
|
|
+
|
|
|
+ return {
|
|
|
+ answers: answers || [],
|
|
|
+ refetchAnswers
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+// 使用react-query管理用户答案历史
|
|
|
+export function useUserAnswerHistory(roomId: string | null, userId: string | null) {
|
|
|
+ const socketClient = useSocketClient(roomId);
|
|
|
|
|
|
const { data: userAnswers, refetch: refetchUserAnswers } = useQuery({
|
|
|
queryKey: ['user-answers', roomId, userId],
|
|
|
queryFn: async () => {
|
|
|
if (!roomId || !userId || !socketClient) return [];
|
|
|
|
|
|
+ // 先获取所有价格记录
|
|
|
const pricesData = await socketClient.client?.redis.hgetall(`quiz:${roomId}:prices`);
|
|
|
if (!pricesData) return [];
|
|
|
|
|
|
+ // 获取所有日期的答案
|
|
|
const allAnswers = await Promise.all(
|
|
|
Object.keys(pricesData).map(date => socketClient.getAnswers(roomId, date))
|
|
|
);
|
|
|
|
|
|
+ // 过滤出当前用户的答案并添加价格信息
|
|
|
const userAnswersWithPrice = allAnswers
|
|
|
.flat()
|
|
|
.filter((answer: Answer) => answer.userId === userId)
|
|
|
@@ -423,8 +437,9 @@ export function useExamUserAnswerHistory(roomId: string | null, userId: string |
|
|
|
price: priceData.price
|
|
|
};
|
|
|
})
|
|
|
- .sort((a: Answer, b: Answer) => new Date(a.date || '').getTime() - new Date(b.date || '').getTime());
|
|
|
+ .sort((a: Answer, b: Answer) => new Date(a.date || '').getTime() - new Date(b.date || '').getTime()); // 按日期排序
|
|
|
|
|
|
+ // 计算每条记录的收益
|
|
|
let totalProfitAmount = 0;
|
|
|
let totalProfitPercent = 0;
|
|
|
const answersWithProfit = userAnswersWithPrice.map((answer: Answer, index: number) => {
|
|
|
@@ -469,9 +484,9 @@ export function useExamUserAnswerHistory(roomId: string | null, userId: string |
|
|
|
};
|
|
|
}
|
|
|
|
|
|
-// 答案提交Hook
|
|
|
-export function useExamAnswerSubmission(roomId: string | null) {
|
|
|
- const { client, sendRoomMessage, storeAnswer } = useExamSocketClient(roomId);
|
|
|
+// 使用react-query管理答案提交
|
|
|
+export function useAnswerSubmission(roomId: string | null) {
|
|
|
+ const { client, sendRoomMessage, storeAnswer } = useSocketClient(roomId);
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
|
const submitAnswer = useCallback(async (date: string, nickname: string, answer: any) => {
|
|
|
@@ -491,9 +506,157 @@ export function useExamAnswerSubmission(roomId: string | null) {
|
|
|
return { submitAnswer };
|
|
|
}
|
|
|
|
|
|
-// 题目管理Hook
|
|
|
-export function useExamQuestionManagement(roomId: string | null) {
|
|
|
- const socketClient = useExamSocketClient(roomId);
|
|
|
+// 使用react-query管理答案提交到后端
|
|
|
+export function useAnswerManagement(roomId: string | null) {
|
|
|
+ const socketClient = useSocketClient(roomId);
|
|
|
+ const queryClient = useQueryClient();
|
|
|
+
|
|
|
+ // 添加自动结算函数
|
|
|
+ const autoSettlement = useCallback(async (date: string) => {
|
|
|
+ if (!socketClient?.client) return;
|
|
|
+
|
|
|
+ return handleAsyncOperation(async () => {
|
|
|
+ // 获取当前所有答案
|
|
|
+ const answers = await socketClient.getAnswers(roomId, date);
|
|
|
+ const currentPrice = answers[0]?.price; // 使用当前价格作为结算价格
|
|
|
+
|
|
|
+ if (!currentPrice) return;
|
|
|
+
|
|
|
+ // 找出所有持股的用户
|
|
|
+ const holdingStockUsers = answers.filter((answer: Answer) => answer.holdingStock === '1');
|
|
|
+
|
|
|
+ // 为每个持股用户创建一个结算记录
|
|
|
+ await Promise.all(holdingStockUsers.map(async (answer: Answer) => {
|
|
|
+ const settlementAnswer = {
|
|
|
+ ...answer,
|
|
|
+ date,
|
|
|
+ holdingStock: '0', // 清仓
|
|
|
+ holdingCash: '1', // 全部持币
|
|
|
+ price: currentPrice,
|
|
|
+ userId: answer.userId,
|
|
|
+ };
|
|
|
+
|
|
|
+ // 存储结算记录
|
|
|
+ await socketClient.storeAnswer(roomId, date, answer.userId, settlementAnswer);
|
|
|
+ }));
|
|
|
+
|
|
|
+ // 发送结算消息通知客户端刷新
|
|
|
+ await socketClient.sendRoomMessage(roomId, {
|
|
|
+ type: 'settlement',
|
|
|
+ content: {
|
|
|
+ date,
|
|
|
+ price: currentPrice
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 刷新当前页面的数据
|
|
|
+ queryClient.invalidateQueries({ queryKey: ['answers', roomId, date] });
|
|
|
+ }, '自动结算失败');
|
|
|
+ }, [roomId, socketClient, queryClient]);
|
|
|
+
|
|
|
+ const submitAnswersToBackend = useCallback(async (date: string) => {
|
|
|
+ if (!socketClient) return;
|
|
|
+
|
|
|
+ return handleAsyncOperation(async () => {
|
|
|
+ const allAnswers = await socketClient.getAnswers(roomId, date);
|
|
|
+
|
|
|
+ // 检查是否还有持股的用户
|
|
|
+ const hasHoldingStock = allAnswers.some((answer: Answer) => answer.holdingStock === '1');
|
|
|
+ if (hasHoldingStock) {
|
|
|
+ throw new Error('还有用户持股中,请先进行结算');
|
|
|
+ }
|
|
|
+
|
|
|
+ const priceData = await socketClient.client?.redis.hget(`quiz:${roomId}:prices`, date);
|
|
|
+ const { price } = priceData ? JSON.parse(priceData) : { price: '0' };
|
|
|
+
|
|
|
+ // 获取前一天的价格
|
|
|
+ const allPrices = await socketClient.client?.redis.hgetall(`quiz:${roomId}:prices`);
|
|
|
+ const dates = Object.keys(allPrices || {}).sort();
|
|
|
+ const currentDateIndex = dates.indexOf(date);
|
|
|
+ const prevPrice = currentDateIndex > 0
|
|
|
+ ? JSON.parse(allPrices![dates[currentDateIndex - 1]]).price
|
|
|
+ : price;
|
|
|
+
|
|
|
+ // 计算每个用户的收益
|
|
|
+ const answersWithProfit = allAnswers.map((answer: Answer) => ({
|
|
|
+ ...answer,
|
|
|
+ price,
|
|
|
+ profit: calculateProfit(parseFloat(price), parseFloat(prevPrice), answer.holdingStock || '0')
|
|
|
+ }));
|
|
|
+
|
|
|
+ const response = await fetch('/api/v1/classroom-answers', {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
+ },
|
|
|
+ body: JSON.stringify({
|
|
|
+ classroom_no: roomId,
|
|
|
+ date,
|
|
|
+ answers: answersWithProfit
|
|
|
+ })
|
|
|
+ });
|
|
|
+
|
|
|
+ const data = await response.json();
|
|
|
+ if (!data.success) {
|
|
|
+ throw new Error(data.message || '提交失败');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 发送收卷消息通知客户端
|
|
|
+ await socketClient.sendRoomMessage(roomId, {
|
|
|
+ type: 'submit',
|
|
|
+ content: {
|
|
|
+ date,
|
|
|
+ price
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ await socketClient.cleanupRoom(roomId, date);
|
|
|
+ return data;
|
|
|
+ }, '提交答案到后端失败');
|
|
|
+ }, [roomId, socketClient]);
|
|
|
+
|
|
|
+ const { data: results, refetch: refetchResults } = useQuery({
|
|
|
+ queryKey: ['training-results', roomId],
|
|
|
+ queryFn: async () => {
|
|
|
+ if (!roomId) return null;
|
|
|
+ const response = await fetch(`/api/v1/classroom-results?classroom_no=${roomId}`);
|
|
|
+ const data = await response.json();
|
|
|
+ if (!data.success) {
|
|
|
+ throw new Error(data.message || '获取结果失败');
|
|
|
+ }
|
|
|
+ return data.data;
|
|
|
+ },
|
|
|
+ enabled: false
|
|
|
+ });
|
|
|
+
|
|
|
+ const restartTraining = useCallback(async () => {
|
|
|
+ if (!socketClient) return;
|
|
|
+
|
|
|
+ return handleAsyncOperation(async () => {
|
|
|
+ await socketClient.cleanupRoom(roomId);
|
|
|
+ // 发送重开消息
|
|
|
+ await socketClient.sendRoomMessage(roomId, {
|
|
|
+ type: 'restart',
|
|
|
+ content: {}
|
|
|
+ });
|
|
|
+ queryClient.invalidateQueries({ queryKey: ['current-question', roomId] });
|
|
|
+ queryClient.invalidateQueries({ queryKey: ['answers', roomId] });
|
|
|
+ queryClient.invalidateQueries({ queryKey: ['training-results', roomId] });
|
|
|
+ }, '重启训练失败');
|
|
|
+ }, [roomId, socketClient, queryClient]);
|
|
|
+
|
|
|
+ return {
|
|
|
+ autoSettlement, // 暴露结算函数
|
|
|
+ submitAnswersToBackend,
|
|
|
+ results,
|
|
|
+ refetchResults,
|
|
|
+ restartTraining
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+// 使用react-query管理题目发送 - 直接使用 useSocketClient 中的 sendNextQuestion
|
|
|
+export function useQuestionManagement(roomId: string | null) {
|
|
|
+ const socketClient = useSocketClient(roomId);
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
|
const sendNextQuestion = useCallback(async (state: QuizState) => {
|
|
|
@@ -508,4 +671,4 @@ export function useExamQuestionManagement(roomId: string | null) {
|
|
|
return {
|
|
|
sendNextQuestion
|
|
|
};
|
|
|
-}
|
|
|
+}
|