feat: 继续重构 Community 子组件和 EventDetail 子组件

This commit is contained in:
zdl
2025-10-18 09:53:26 +08:00
parent c1bea7a75d
commit 3a3cac75f7
2 changed files with 133 additions and 67 deletions

View File

@@ -15,6 +15,7 @@ import EventDiscussionModal from './EventDiscussionModal';
import { useSubscription } from '../../../hooks/useSubscription'; import { useSubscription } from '../../../hooks/useSubscription';
import SubscriptionUpgradeModal from '../../../components/SubscriptionUpgradeModal'; import SubscriptionUpgradeModal from '../../../components/SubscriptionUpgradeModal';
import moment from 'moment'; import moment from 'moment';
import { logger } from '../../../utils/logger';
const { Title, Text } = Typography; const { Title, Text } = Typography;
const { TabPane } = Tabs; const { TabPane } = Tabs;
@@ -58,24 +59,24 @@ const shouldRefreshData = (cacheKey) => {
// 获取K线数据带缓存和防重复请求 // 获取K线数据带缓存和防重复请求
const fetchKlineData = async (stockCode, eventTime) => { const fetchKlineData = async (stockCode, eventTime) => {
const cacheKey = getCacheKey(stockCode, eventTime); const cacheKey = getCacheKey(stockCode, eventTime);
// 1. 检查缓存 // 1. 检查缓存
if (klineDataCache.has(cacheKey)) { if (klineDataCache.has(cacheKey)) {
// 检查是否需要刷新 // 检查是否需要刷新
if (!shouldRefreshData(cacheKey)) { if (!shouldRefreshData(cacheKey)) {
console.log(`使用缓存数据: ${cacheKey}`); logger.debug('StockDetailPanel', '使用缓存数据', { cacheKey });
return klineDataCache.get(cacheKey); return klineDataCache.get(cacheKey);
} }
} }
// 2. 检查是否有正在进行的请求 // 2. 检查是否有正在进行的请求
if (pendingRequests.has(cacheKey)) { if (pendingRequests.has(cacheKey)) {
console.log(`等待进行中的请求: ${cacheKey}`); logger.debug('StockDetailPanel', '等待进行中的请求', { cacheKey });
return pendingRequests.get(cacheKey); return pendingRequests.get(cacheKey);
} }
// 3. 发起新请求 // 3. 发起新请求
console.log(`发起新请求: ${cacheKey}`); logger.debug('StockDetailPanel', '发起新K线数据请求', { cacheKey });
const normalizedEventTime = eventTime ? moment(eventTime).format('YYYY-MM-DD HH:mm') : undefined; const normalizedEventTime = eventTime ? moment(eventTime).format('YYYY-MM-DD HH:mm') : undefined;
const requestPromise = stockService const requestPromise = stockService
.getKlineData(stockCode, 'minute', normalizedEventTime) .getKlineData(stockCode, 'minute', normalizedEventTime)
@@ -86,11 +87,14 @@ const fetchKlineData = async (stockCode, eventTime) => {
lastRequestTime.set(cacheKey, Date.now()); lastRequestTime.set(cacheKey, Date.now());
// 清除pending状态 // 清除pending状态
pendingRequests.delete(cacheKey); pendingRequests.delete(cacheKey);
console.log(`请求完成并缓存: ${cacheKey}`); logger.debug('StockDetailPanel', 'K线数据请求完成并缓存', {
cacheKey,
dataPoints: data.length
});
return data; return data;
}) })
.catch((error) => { .catch((error) => {
console.error(`获取${stockCode}的K线数据失败:`, error); logger.error('StockDetailPanel', 'fetchKlineData', error, { stockCode, cacheKey });
// 清除pending状态 // 清除pending状态
pendingRequests.delete(cacheKey); pendingRequests.delete(cacheKey);
// 如果有旧缓存,返回旧数据 // 如果有旧缓存,返回旧数据
@@ -99,10 +103,10 @@ const fetchKlineData = async (stockCode, eventTime) => {
} }
return []; return [];
}); });
// 保存pending请求 // 保存pending请求
pendingRequests.set(cacheKey, requestPromise); pendingRequests.set(cacheKey, requestPromise);
return requestPromise; return requestPromise;
}; };
@@ -284,7 +288,11 @@ const StockDetailModal = ({ stock, onClose, fixed, eventTime }) => {
}; };
function StockDetailPanel({ visible, event, onClose }) { function StockDetailPanel({ visible, event, onClose }) {
console.log('StockDetailPanel 组件已加载visible:', visible, 'event:', event?.id); logger.debug('StockDetailPanel', '组件加载', {
visible,
eventId: event?.id,
eventTitle: event?.title
});
// 权限控制 // 权限控制
const { hasFeatureAccess, getRequiredLevel, getUpgradeRecommendation } = useSubscription(); const { hasFeatureAccess, getRequiredLevel, getUpgradeRecommendation } = useSubscription();
@@ -362,7 +370,10 @@ function StockDetailPanel({ visible, event, onClose }) {
const codes = relatedStocks.map(s => s.stock_code); const codes = relatedStocks.map(s => s.stock_code);
stockService.getQuotes(codes, event?.created_at) stockService.getQuotes(codes, event?.created_at)
.then(quotes => setStockQuotes(quotes)) .then(quotes => setStockQuotes(quotes))
.catch(error => console.error('更新行情失败:', error)); .catch(error => logger.error('StockDetailPanel', 'updateQuotes', error, {
stockCodes: codes,
eventTime: event?.created_at
}));
}; };
updateQuotes(); updateQuotes();
@@ -391,9 +402,12 @@ function StockDetailPanel({ visible, event, onClose }) {
if (data.success && data.data) { if (data.success && data.data) {
const watchlistSet = new Set(data.data.map(item => item.stock_code)); const watchlistSet = new Set(data.data.map(item => item.stock_code));
setWatchlistStocks(watchlistSet); setWatchlistStocks(watchlistSet);
logger.debug('StockDetailPanel', '自选股列表加载成功', {
count: watchlistSet.size
});
} }
} catch (error) { } catch (error) {
console.error('加载自选股列表失败:', error); logger.error('StockDetailPanel', 'loadWatchlist', error);
} }
}, []); }, []);
@@ -452,7 +466,10 @@ function StockDetailPanel({ visible, event, onClose }) {
// 初始化数据加载 // 初始化数据加载
useEffect(() => { useEffect(() => {
console.log('[StockDetailPanel] useEffect 触发, visible:', visible, 'event:', event?.id); logger.debug('StockDetailPanel', 'useEffect 触发', {
visible,
eventId: event?.id
});
if (visible && event) { if (visible && event) {
setActiveTab('stocks'); setActiveTab('stocks');
loadAllData(); loadAllData();
@@ -461,27 +478,40 @@ function StockDetailPanel({ visible, event, onClose }) {
// 加载所有数据的函数 // 加载所有数据的函数
const loadAllData = useCallback(() => { const loadAllData = useCallback(() => {
console.log('[StockDetailPanel] loadAllData 被调用, event:', event?.id); logger.debug('StockDetailPanel', 'loadAllData 被调用', {
eventId: event?.id
});
if (!event) return; if (!event) return;
// 加载自选股列表 // 加载自选股列表
loadWatchlist(); loadWatchlist();
// 加载相关标的 // 加载相关标的
setLoading(true); setLoading(true);
eventService.getRelatedStocks(event.id) eventService.getRelatedStocks(event.id)
.then(res => { .then(res => {
console.log('[前端] 接收到事件相关股票数据:', res); logger.debug('StockDetailPanel', '接收到事件相关股票数据', {
eventId: event.id,
success: res.success,
stockCount: res.data?.length || 0
});
if (res.success) { if (res.success) {
console.log('[前端] 股票数据数组:', res.data); if (res.data && res.data[0]) {
console.log('[前端] 第一只股票:', res.data[0]); logger.debug('StockDetailPanel', '第一只股票数据', {
console.log('[前端] 第一只股票的 relation_desc:', res.data[0]?.relation_desc); stockCode: res.data[0].stock_code,
stockName: res.data[0].stock_name,
hasRelationDesc: !!res.data[0].relation_desc
});
}
setRelatedStocks(res.data); setRelatedStocks(res.data);
if (res.data.length > 0) { if (res.data.length > 0) {
const codes = res.data.map(s => s.stock_code); const codes = res.data.map(s => s.stock_code);
stockService.getQuotes(codes, event.created_at) stockService.getQuotes(codes, event.created_at)
.then(quotes => setStockQuotes(quotes)) .then(quotes => setStockQuotes(quotes))
.catch(error => console.error('加载行情失败:', error)); .catch(error => logger.error('StockDetailPanel', 'getQuotes', error, {
stockCodes: codes,
eventTime: event.created_at
}));
} }
} }
}) })
@@ -593,7 +623,10 @@ function StockDetailPanel({ visible, event, onClose }) {
key: 'relation_desc', key: 'relation_desc',
width: 300, width: 300,
render: (relationDesc, record) => { render: (relationDesc, record) => {
console.log('[表格渲染] 股票:', record.stock_code, 'relation_desc:', relationDesc); logger.debug('StockDetailPanel', '表格渲染 - 股票关联描述', {
stockCode: record.stock_code,
hasRelationDesc: !!relationDesc
});
// 处理 relation_desc 的两种格式 // 处理 relation_desc 的两种格式
let text = ''; let text = '';
@@ -611,7 +644,10 @@ function StockDetailPanel({ visible, event, onClose }) {
.filter(s => s) .filter(s => s)
.join('') || '--'; .join('') || '--';
} else { } else {
console.warn('[表格渲染] 未知的 relation_desc 格式:', relationDesc); logger.warn('StockDetailPanel', '未知的 relation_desc 格式', {
stockCode: record.stock_code,
relationDescType: typeof relationDesc
});
return '--'; return '--';
} }

View File

@@ -24,6 +24,7 @@ import {
Divider, Divider,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { logger } from '../../../utils/logger';
// Custom components // Custom components
import Card from 'components/Card/Card.js'; import Card from 'components/Card/Card.js';
@@ -100,34 +101,35 @@ function Subscription() {
const fetchSubscriptionPlans = async () => { const fetchSubscriptionPlans = async () => {
try { try {
console.log('🔄 正在获取订阅套餐...'); logger.debug('Subscription', '正在获取订阅套餐');
const response = await fetch('/api/subscription/plans'); const response = await fetch('/api/subscription/plans');
console.log('📡 套餐API响应状态:', response.status);
if (response.ok) { if (response.ok) {
const data = await response.json(); const data = await response.json();
console.log('📦 套餐API响应数据:', data);
if (data.success && Array.isArray(data.data)) { if (data.success && Array.isArray(data.data)) {
// 确保每个套餐都有必要的字段 // 确保每个套餐都有必要的字段
const validPlans = data.data.filter(plan => const validPlans = data.data.filter(plan =>
plan && plan &&
plan.name && plan.name &&
typeof plan.monthly_price === 'number' && typeof plan.monthly_price === 'number' &&
typeof plan.yearly_price === 'number' typeof plan.yearly_price === 'number'
); );
console.log('✅ 有效套餐数量:', validPlans.length); logger.debug('Subscription', '套餐加载成功', {
status: response.status,
validPlansCount: validPlans.length
});
setSubscriptionPlans(validPlans); setSubscriptionPlans(validPlans);
} else { } else {
console.warn('⚠️ 套餐数据格式异常:', data); logger.warn('Subscription', '套餐数据格式异常', { data });
setSubscriptionPlans([]); setSubscriptionPlans([]);
} }
} else { } else {
console.error('❌ 获取套餐失败,状态码:', response.status); logger.error('Subscription', 'fetchSubscriptionPlans', new Error(`HTTP ${response.status}`));
setSubscriptionPlans([]); setSubscriptionPlans([]);
} }
} catch (error) { } catch (error) {
console.error('❌ 获取订阅套餐失败:', error); logger.error('Subscription', 'fetchSubscriptionPlans', error);
setSubscriptionPlans([]); setSubscriptionPlans([]);
} }
}; };
@@ -139,14 +141,18 @@ function Subscription() {
}); });
if (response.ok) { if (response.ok) {
const data = await response.json(); const data = await response.json();
console.log('👤 用户数据:', data); logger.debug('Subscription', '用户数据获取成功', { data });
if (data.success) { if (data.success) {
setCurrentUser(data.user); setCurrentUser(data.user);
console.log('用户信息已更新:', data.user); logger.debug('Subscription', '用户信息已更新', {
userId: data.user?.id,
subscriptionType: data.user?.subscription_type,
subscriptionStatus: data.user?.subscription_status
});
} }
} }
} catch (error) { } catch (error) {
console.error('获取用户信息失败:', error); logger.error('Subscription', 'fetchCurrentUser', error);
} }
}; };
@@ -243,23 +249,28 @@ function Subscription() {
// 自动检查支付状态 // 自动检查支付状态
const startAutoPaymentCheck = (orderId) => { const startAutoPaymentCheck = (orderId) => {
console.log('🔄 开始自动检查支付状态...'); logger.info('Subscription', '开始自动检查支付状态', { orderId });
const checkInterval = setInterval(async () => { const checkInterval = setInterval(async () => {
try { try {
const response = await fetch(`/api/payment/order/${orderId}/status`, { const response = await fetch(`/api/payment/order/${orderId}/status`, {
credentials: 'include' credentials: 'include'
}); });
if (response.ok) { if (response.ok) {
const data = await response.json(); const data = await response.json();
console.log('📡 支付状态检查结果:', data); logger.debug('Subscription', '支付状态检查结果', {
orderId,
paymentSuccess: data.payment_success,
data
});
if (data.success && data.payment_success) { if (data.success && data.payment_success) {
// 支付成功 // 支付成功
clearInterval(checkInterval); clearInterval(checkInterval);
setAutoCheckInterval(null); setAutoCheckInterval(null);
logger.info('Subscription', '自动检测到支付成功', { orderId });
toast({ toast({
title: '支付成功!', title: '支付成功!',
description: '订阅已激活,正在跳转...', description: '订阅已激活,正在跳转...',
@@ -267,7 +278,7 @@ function Subscription() {
duration: 3000, duration: 3000,
isClosable: true, isClosable: true,
}); });
// 延迟2秒后跳转到个人中心 // 延迟2秒后跳转到个人中心
setTimeout(() => { setTimeout(() => {
onPaymentModalClose(); onPaymentModalClose();
@@ -276,10 +287,10 @@ function Subscription() {
} }
} }
} catch (error) { } catch (error) {
console.error('自动检查支付状态失败:', error); logger.error('Subscription', 'startAutoPaymentCheck', error, { orderId });
} }
}, 10000); // 每10秒检查一次 }, 10000); // 每10秒检查一次
setAutoCheckInterval(checkInterval); setAutoCheckInterval(checkInterval);
}; };
@@ -287,7 +298,7 @@ function Subscription() {
if (autoCheckInterval) { if (autoCheckInterval) {
clearInterval(autoCheckInterval); clearInterval(autoCheckInterval);
setAutoCheckInterval(null); setAutoCheckInterval(null);
console.log('⏹️ 停止自动检查支付状态'); logger.debug('Subscription', '停止自动检查支付状态');
} }
}; };
@@ -316,22 +327,26 @@ function Subscription() {
// 强制更新支付状态 // 强制更新支付状态
const handleForceUpdatePayment = async () => { const handleForceUpdatePayment = async () => {
if (!paymentOrder) return; if (!paymentOrder) return;
setForceUpdating(true); setForceUpdating(true);
try { try {
const response = await fetch(`/api/payment/order/${paymentOrder.id}/force-update`, { const response = await fetch(`/api/payment/order/${paymentOrder.id}/force-update`, {
method: 'POST', method: 'POST',
credentials: 'include' credentials: 'include'
}); });
if (response.ok) { if (response.ok) {
const data = await response.json(); const data = await response.json();
console.log('🔧 强制更新支付状态结果:', data); logger.info('Subscription', '强制更新支付状态结果', {
orderId: paymentOrder.id,
paymentSuccess: data.payment_success,
data
});
if (data.success && data.payment_success) { if (data.success && data.payment_success) {
// 支付成功 // 支付成功
stopAutoPaymentCheck(); stopAutoPaymentCheck();
toast({ toast({
title: '状态更新成功!', title: '状态更新成功!',
description: '订阅已激活,正在刷新页面...', description: '订阅已激活,正在刷新页面...',
@@ -339,7 +354,7 @@ function Subscription() {
duration: 3000, duration: 3000,
isClosable: true, isClosable: true,
}); });
setTimeout(() => { setTimeout(() => {
onPaymentModalClose(); onPaymentModalClose();
window.location.reload(); window.location.reload();
@@ -357,6 +372,9 @@ function Subscription() {
throw new Error('网络错误'); throw new Error('网络错误');
} }
} catch (error) { } catch (error) {
logger.error('Subscription', 'handleForceUpdatePayment', error, {
orderId: paymentOrder?.id
});
toast({ toast({
title: '强制更新失败', title: '强制更新失败',
description: error.message, description: error.message,
@@ -372,24 +390,29 @@ function Subscription() {
// 手动检查支付状态 // 手动检查支付状态
const handleCheckPaymentStatus = async () => { const handleCheckPaymentStatus = async () => {
if (!paymentOrder) return; if (!paymentOrder) return;
setCheckingPayment(true); setCheckingPayment(true);
try { try {
const response = await fetch(`/api/payment/order/${paymentOrder.id}/status`, { const response = await fetch(`/api/payment/order/${paymentOrder.id}/status`, {
credentials: 'include' credentials: 'include'
}); });
if (response.ok) { if (response.ok) {
const data = await response.json(); const data = await response.json();
console.log('🔍 手动检查支付状态结果:', data); logger.info('Subscription', '手动检查支付状态结果', {
console.log('🔍 支付成功标志:', data.payment_success); orderId: paymentOrder.id,
console.log('🔍 订单数据:', data.data); paymentSuccess: data.payment_success,
data: data.data
});
if (data.success) { if (data.success) {
if (data.payment_success) { if (data.payment_success) {
// 支付成功 // 支付成功
stopAutoPaymentCheck(); stopAutoPaymentCheck();
logger.info('Subscription', '手动检测到支付成功', {
orderId: paymentOrder.id
});
toast({ toast({
title: '支付成功!', title: '支付成功!',
description: '订阅已激活,正在跳转...', description: '订阅已激活,正在跳转...',
@@ -397,12 +420,12 @@ function Subscription() {
duration: 3000, duration: 3000,
isClosable: true, isClosable: true,
}); });
setTimeout(() => { setTimeout(() => {
onPaymentModalClose(); onPaymentModalClose();
window.location.reload(); window.location.reload();
}, 2000); }, 2000);
} else { } else {
// 还未支付 // 还未支付
toast({ toast({
@@ -420,6 +443,9 @@ function Subscription() {
throw new Error('网络错误'); throw new Error('网络错误');
} }
} catch (error) { } catch (error) {
logger.error('Subscription', 'handleCheckPaymentStatus', error, {
orderId: paymentOrder?.id
});
toast({ toast({
title: '查询失败', title: '查询失败',
description: error.message, description: error.message,
@@ -788,8 +814,8 @@ function Subscription() {
size='sm' size='sm'
variant='ghost' variant='ghost'
onClick={() => { onClick={() => {
console.log('🔍 当前支付订单:', paymentOrder); logger.debug('Subscription', '调试信息 - 当前支付订单', { paymentOrder });
console.log('🔍 用户信息:', currentUser); logger.debug('Subscription', '调试信息 - 用户信息', { currentUser });
}} }}
> >
调试信息 调试信息
@@ -870,7 +896,11 @@ function Subscription() {
</VStack> </VStack>
<Divider my={3} /> <Divider my={3} />
<HStack spacing={2}> <HStack spacing={2}>
<Button size='sm' onClick={() => console.log('当前状态:', { currentUser, paymentOrder, autoCheckInterval })}> <Button size='sm' onClick={() => logger.debug('Subscription', '调试 - 当前状态', {
currentUser,
paymentOrder,
autoCheckInterval: autoCheckInterval ? '运行中' : '已停止'
})}>
打印状态 打印状态
</Button> </Button>
<Button size='sm' onClick={handleRefreshUserStatus}> <Button size='sm' onClick={handleRefreshUserStatus}>