ソースを参照

优化消息订阅逻辑

yourname 6 ヶ月 前
コミット
d55baad585

+ 71 - 79
client/mobile/components/Exam/ExamAdmin.tsx

@@ -72,10 +72,10 @@ export default function ExamAdmin() {
   const [searchParams] = useSearchParams();
   const classroom = searchParams.get('classroom');
   const {
-    socketRoom,
+    socketRoom: { joinRoom, leaveRoom, client },
     answerManagement,
-    currentQuestion,
-    setCurrentQuestion,
+    // currentQuestion,
+    // setCurrentQuestion,
     // calculateCumulativeResults
   } = useSocketClient(classroom as string);
 
@@ -96,7 +96,8 @@ export default function ExamAdmin() {
       // 获取当前问题
       const question = await answerManagement.getCurrentQuestion(classroom);
       if (question) {
-        setCurrentQuestion(question);
+        setCurrentDate(question.date);
+        setCurrentPrice(String(question.price));
 
         // 获取答题记录
         const answers = await answerManagement.getAnswers(
@@ -125,81 +126,6 @@ export default function ExamAdmin() {
       setLoading(false);
     }
   };
-  
-  // 加入/离开房间
-  useEffect(() => {
-    if (!classroom) return;
-    
-    socketRoom.joinRoom(classroom);
-    initExamData();
-
-    return () => {
-      socketRoom.leaveRoom(classroom);
-    };
-  }, [classroom, socketRoom.joinRoom, socketRoom.leaveRoom]);
-
-
-  // 监听答题消息并更新答案
-  useEffect(() => {
-    if (!classroom || !currentDate || !socketRoom.client) return;
-
-    const handleAnswerMessage = async () => {
-      try {
-        const answers = await answerManagement.getAnswers(
-          classroom as string,
-          currentDate
-        );
-        
-        const processedAnswers = answers.map(answer => ({
-          ...answer,
-          profitAmount: answer.profitAmount || 0,
-          profitPercent: answer.profitPercent || 0,
-          holdingStock: answer.holdingStock || '0',
-          holdingCash: answer.holdingCash || '0'
-        }));
-
-        setAnswers(processedAnswers);
-        setDailyAnswers(prev => ({
-          ...prev,
-          [currentDate]: processedAnswers
-        }));
-      } catch (error) {
-        console.error('获取答案失败:', error);
-      }
-    };
-
-    socketRoom.client.on('exam:answer', handleAnswerMessage);
-
-    return () => {
-      if (!socketRoom.client) return;
-      socketRoom.client.off('exam:answer', handleAnswerMessage);
-    };
-  }, [classroom, currentDate, answerManagement, socketRoom]);
-
-  // // 更新答案状态
-  // useEffect(() => {
-  //   if (cachedAnswers && cachedAnswers.length > 0) {
-  //     setAnswers(cachedAnswers);
-  //   }
-  // }, [cachedAnswers]);
-
-  // // 更新每日答题情况
-  // useEffect(() => {
-  //   if (currentDate && cachedAnswers) {
-  //     setDailyAnswers((prev: {[key: string]: Answer[]}) => ({
-  //       ...prev,
-  //       [currentDate]: cachedAnswers
-  //     }));
-  //   }
-  // }, [currentDate, cachedAnswers]);
-
-  useEffect(() => {
-    if (currentQuestion) {
-      console.log('currentQuestion', currentQuestion);
-      setCurrentDate(currentQuestion.date);
-      setCurrentPrice(String(currentQuestion.price));
-    }
-  }, [currentQuestion]);
 
   // 添加结算函数
   const handleSettlement = async () => {
@@ -389,6 +315,72 @@ export default function ExamAdmin() {
     },
   ];
 
+  
+  // 加入/离开房间
+  useEffect(() => {
+    if (!classroom) return;
+    
+    joinRoom(classroom);
+    initExamData();
+
+    return () => {
+      leaveRoom(classroom);
+    };
+  }, [classroom, joinRoom, leaveRoom]);
+
+
+  // 监听答题消息并更新答案
+  useEffect(() => {
+    if (!classroom || !currentDate || !client) return;
+
+    const handleAnswerMessage = async () => {
+      try {
+        const answers = await answerManagement.getAnswers(
+          classroom as string,
+          currentDate
+        );
+        
+        const processedAnswers = answers.map(answer => ({
+          ...answer,
+          profitAmount: answer.profitAmount || 0,
+          profitPercent: answer.profitPercent || 0,
+          holdingStock: answer.holdingStock || '0',
+          holdingCash: answer.holdingCash || '0'
+        }));
+
+        setAnswers(processedAnswers);
+        setDailyAnswers(prev => ({
+          ...prev,
+          [currentDate]: processedAnswers
+        }));
+      } catch (error) {
+        console.error('获取答案失败:', error);
+      }
+    };
+
+    client.on('exam:answerUpdated', handleAnswerMessage);
+
+    return () => {
+      if (!client) return;
+      client.off('exam:answerUpdated', handleAnswerMessage);
+    };
+  }, [classroom, currentDate, answerManagement, client]);
+
+  // 监听当前问题变化
+  useEffect(() => {
+    if (!client ) return;
+
+    const handleQuestionUpdate = (question:QuizState ) => {
+      setCurrentDate(question.date);
+      setCurrentPrice(String(question.price));
+    };
+
+    client.on('exam:question', handleQuestionUpdate);
+    return () => {
+      client.off('exam:question', handleQuestionUpdate);
+    };
+  }, [client]);
+
   return (
     <div className="p-6">
       <div className="mb-6 flex justify-between items-center">

+ 49 - 74
client/mobile/components/Exam/ExamCard.tsx

@@ -6,9 +6,8 @@ import { message } from 'antd';
 import { useSocketClient } from './hooks/useSocketClient.ts';
 import { ClassroomDataAPI } from '../../api/classroom_data.ts';
 import { ClassroomStatus } from '../../../share/types_stock.ts';
-import type { ExamSocketRoomMessage } from './types.ts';
+import type { QuizState } from './types.ts';
 import type { AnswerRecord, Answer } from './types.ts';
-import type { ClassroomData } from '../../../share/types_stock.ts';
 import { useAuth } from "../../hooks.tsx";
 
 
@@ -21,12 +20,9 @@ export default function ExamCard() {
   const [searchParams] = useSearchParams();
   const classroom = searchParams.get('classroom');
   const {
-    socketRoom: { joinRoom, leaveRoom },
+    socketRoom: { joinRoom, leaveRoom, client },
     answerManagement,
-    currentQuestion,
-    setCurrentQuestion,
     lastMessage,
-    userAnswers
   } = useSocketClient(classroom as string);
   const [currentDate, setCurrentDate] = useState('');
   const [currentPrice, setCurrentPrice] = useState('0');
@@ -65,12 +61,10 @@ export default function ExamCard() {
     }
     
     // 获取当前问题并更新状态
-    try {
-      const question = await answerManagement.getCurrentQuestion(classroom);
-      setCurrentQuestion(question);
-    } catch (error) {
-      console.error('获取当前问题失败:', error);
-    }
+    const question = await answerManagement.getCurrentQuestion(classroom);
+    setCurrentDate(question.date);
+    setCurrentPrice(String(question.price));
+    setIsStarted(true);
     
     // 获取用户回答记录
     if (user?.id) {
@@ -129,26 +123,12 @@ export default function ExamCard() {
     }
   }, [lastMessage]);
 
-  useEffect(() => {
-    if (currentQuestion) {
-      console.log('currentQuestion', currentQuestion);
-      setCurrentDate(currentQuestion.date);
-      setCurrentPrice(String(currentQuestion.price));
-      setIsStarted(true);
-    } else {
-      // 如果没有当前问题,重置状态
-      setCurrentDate('');
-      setCurrentPrice('0');
-      setIsStarted(false);
-    }
-  }, [currentQuestion]);
-
   // 处理选择A(持股)
   const handleChooseA = useCallback(async () => {
     setHoldingStock('1');
     setHoldingCash('0');
     
-    if (classroom && user?.username) {
+    if (classroom && user?.username && currentDate) {
       const answer = {
         date: currentDate,
         holdingStock: '1',
@@ -160,28 +140,33 @@ export default function ExamCard() {
       try {
         await answerManagement.storeAnswer(
           classroom as string,
-          currentQuestion?.id || '',
-          user.username,
+          currentDate,
+          String(user.id),
           answer,
-          (success) => {
-            if (success) {
-              message.success('答案提交成功');
-            } else {
-              message.error('提交答案失败');
-            }
+          (answers) => {
+            const records = answers.map((answer: Answer, index: number): AnswerRecord => ({
+              date: answer.date,
+              price: String(answer.price || '0'),
+              holdingStock: answer.holdingStock,
+              holdingCash: answer.holdingCash,
+              profitAmount: answer.profitAmount || 0,
+              profitPercent: answer.profitPercent || 0,
+              index: index + 1
+            }));
+            setAnswerRecords(records);
           }
         );
       } catch (error) {
         message.error('提交答案失败');
       }
     }
-  }, [classroom, user, currentDate, currentPrice, answerManagement, currentQuestion]);
+  }, [classroom, user, currentDate, currentPrice, answerManagement]);
 
   const handleChooseB = useCallback(async () => {
     setHoldingStock('0');
     setHoldingCash('1');
     
-    if (classroom && user?.username) {
+    if (classroom && user?.username && currentDate) {
       const answer = {
         date: currentDate,
         holdingStock: '0',
@@ -193,53 +178,43 @@ export default function ExamCard() {
       try {
         await answerManagement.storeAnswer(
           classroom as string,
-          currentQuestion?.id || '',
+          currentDate,
           String(user.id),
           answer,
-          (success) => {
-            if (success) {
-              message.success('答案提交成功');
-            } else {
-              message.error('提交答案失败');
-            }
+          (answers) => {
+            const records = answers.map((answer: Answer, index: number): AnswerRecord => ({
+              date: answer.date,
+              price: String(answer.price || '0'),
+              holdingStock: answer.holdingStock,
+              holdingCash: answer.holdingCash,
+              profitAmount: answer.profitAmount || 0,
+              profitPercent: answer.profitPercent || 0,
+              index: index + 1
+            }));
+            setAnswerRecords(records);
           }
         );
       } catch (error) {
         message.error('提交答案失败');
       }
     }
-  }, [classroom, user, currentDate, currentPrice, answerManagement, currentQuestion]);
+  }, [classroom, user, currentDate, currentPrice, answerManagement]);
 
-  // // 监听socket消息更新答题记录
-  // useEffect(() => {
-  //   if (!lastMessage?.message) return;
+  // 监听当前问题变化
+  useEffect(() => {
+    if (!client ) return;
 
-  //   const { type } = lastMessage.message;
-  //   if (type === 'answer' || type === 'question' || type === 'restart') {
-  //     if (classroom && user?.id) {
-  //       answerManagement.getUserAnswers(classroom, String(user.id))
-  //         .then(answers => {
-  //           if (answers && answers.length > 0) {
-  //             const lastAnswer = answers[answers.length - 1];
-  //             setHoldingStock(lastAnswer.holdingStock);
-  //             setHoldingCash(lastAnswer.holdingCash);
-              
-  //             const records = answers.map((answer: Answer, index: number): AnswerRecord => ({
-  //               date: answer.date,
-  //               price: String(answer.price || '0'),
-  //               holdingStock: answer.holdingStock,
-  //               holdingCash: answer.holdingCash,
-  //               profitAmount: answer.profitAmount || 0,
-  //               profitPercent: answer.profitPercent || 0,
-  //               index: index + 1
-  //             }));
-              
-  //             setAnswerRecords(records);
-  //           }
-  //         });
-  //     }
-  //   }
-  // }, [lastMessage, classroom, user, answerManagement]);
+    const handleQuestionUpdate = (question:QuizState ) => {
+      setCurrentDate(question.date);
+      setCurrentPrice(String(question.price));
+      setIsStarted(true);
+    };
+
+    client.on('exam:question', handleQuestionUpdate);
+    return () => {
+      client.off('exam:question', handleQuestionUpdate);
+    };
+  }, [client]);
 
   if (isLoading || !classroomData) {
     return <div className="flex items-center justify-center min-h-screen">加载中...</div>;

+ 18 - 26
client/mobile/components/Exam/hooks/useSocketClient.ts

@@ -60,9 +60,8 @@ export function useSocketClient(roomId: string | null) {
   const { token } = useAuth();
   const queryClient = useQueryClient();
   const [socket, setSocket] = useState<Socket | null>(null);
-  const [currentQuestion, setCurrentQuestion] = useState<QuizState | null>(null);
+  // const [currentQuestion, setCurrentQuestion] = useState<QuizState | null>(null);
   const [lastMessage, setLastMessage] = useState<ExamSocketRoomMessage | null>(null);
-  const [userAnswers, setUserAnswers] = useState<Answer[]>([]);
 
   // 初始化socket连接
   const { data: client } = useQuery({
@@ -134,19 +133,19 @@ export function useSocketClient(roomId: string | null) {
     }
   }, [client]);
 
-  // 监听当前问题变化
-  useEffect(() => {
-    if (!client || !roomId) return;
+  // // 监听当前问题变化
+  // useEffect(() => {
+  //   if (!client || !roomId) return;
 
-    const handleQuestionUpdate = (question:QuizState ) => {
-      setCurrentQuestion(question);
-    };
+  //   const handleQuestionUpdate = (question:QuizState ) => {
+  //     setCurrentQuestion(question);
+  //   };
 
-    client.on('exam:question', handleQuestionUpdate);
-    return () => {
-      client.off('exam:question', handleQuestionUpdate);
-    };
-  }, [client, roomId]);
+  //   client.on('exam:question', handleQuestionUpdate);
+  //   return () => {
+  //     client.off('exam:question', handleQuestionUpdate);
+  //   };
+  // }, [client, roomId]);
 
   // // 监听用户答案变化
   // useEffect(() => {
@@ -165,7 +164,7 @@ export function useSocketClient(roomId: string | null) {
 
 
   // 存储答案
-  const storeAnswer = useCallback(async (roomId: string, questionId: string, userId: string, answer: QuizContent, callback?: (success: boolean) => void) => {
+  const storeAnswer = useCallback(async (roomId: string, questionId: string, userId: string, answer: QuizContent, callback?: (success: Answer[]) => void) => {
     if (!client) return;
 
     return handleAsyncOperation(async () => {
@@ -193,7 +192,7 @@ export function useSocketClient(roomId: string | null) {
           userId,
           answer: initialAnswer
         }, (success: boolean) => {
-          callback?.(success);
+          callback?.([initialAnswer]);
         });
         return;
       }
@@ -202,6 +201,7 @@ export function useSocketClient(roomId: string | null) {
       const dates = Object.keys(pricesData).sort();
       const allUserAnswers = await getUserAnswers(roomId, userId);
       const userAnswers = allUserAnswers
+        .filter((a: Answer) => a.date !== answer.date)
         .filter((a: Answer) => dates.includes(a.date || ''))
         .map((a: Answer) => ({
           ...a,
@@ -240,13 +240,7 @@ export function useSocketClient(roomId: string | null) {
         userId,
         answer: answerWithProfit
       }, (success: boolean) => {
-        callback?.(success);
-
-        
-        // 提交成功后获取最新回答记录
-        if (success && roomId && answerWithProfit.userId) {
-          setUserAnswers([...userAnswers, answerWithProfit])
-        }
+        callback?.([...userAnswers, answerWithProfit]);
       });
     }, '存储答案失败');
   }, [client]);
@@ -404,11 +398,9 @@ export function useSocketClient(roomId: string | null) {
     socketRoom,
     answerManagement,
     // calculateCumulativeResults,
-    currentQuestion,
-    setCurrentQuestion,
+    // currentQuestion,
+    // setCurrentQuestion,
     lastMessage,
-    userAnswers,
-    // 兼容旧版导入
     ...socketRoom,
     ...answerManagement
   };