feat: 继续重构 Community 子组件和 EventDetail 子组件
This commit is contained in:
@@ -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;
|
||||||
@@ -63,19 +64,19 @@ const fetchKlineData = async (stockCode, eventTime) => {
|
|||||||
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);
|
||||||
// 如果有旧缓存,返回旧数据
|
// 如果有旧缓存,返回旧数据
|
||||||
@@ -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,7 +478,9 @@ 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;
|
||||||
|
|
||||||
// 加载自选股列表
|
// 加载自选股列表
|
||||||
@@ -471,17 +490,28 @@ function StockDetailPanel({ visible, event, onClose }) {
|
|||||||
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 '--';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,13 +101,11 @@ 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)) {
|
||||||
// 确保每个套餐都有必要的字段
|
// 确保每个套餐都有必要的字段
|
||||||
@@ -116,18 +115,21 @@ function Subscription() {
|
|||||||
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,7 +249,7 @@ function Subscription() {
|
|||||||
|
|
||||||
// 自动检查支付状态
|
// 自动检查支付状态
|
||||||
const startAutoPaymentCheck = (orderId) => {
|
const startAutoPaymentCheck = (orderId) => {
|
||||||
console.log('🔄 开始自动检查支付状态...');
|
logger.info('Subscription', '开始自动检查支付状态', { orderId });
|
||||||
|
|
||||||
const checkInterval = setInterval(async () => {
|
const checkInterval = setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -253,13 +259,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', '支付状态检查结果', {
|
||||||
|
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: '订阅已激活,正在跳转...',
|
||||||
@@ -276,7 +287,7 @@ function Subscription() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('自动检查支付状态失败:', error);
|
logger.error('Subscription', 'startAutoPaymentCheck', error, { orderId });
|
||||||
}
|
}
|
||||||
}, 10000); // 每10秒检查一次
|
}, 10000); // 每10秒检查一次
|
||||||
|
|
||||||
@@ -287,7 +298,7 @@ function Subscription() {
|
|||||||
if (autoCheckInterval) {
|
if (autoCheckInterval) {
|
||||||
clearInterval(autoCheckInterval);
|
clearInterval(autoCheckInterval);
|
||||||
setAutoCheckInterval(null);
|
setAutoCheckInterval(null);
|
||||||
console.log('⏹️ 停止自动检查支付状态');
|
logger.debug('Subscription', '停止自动检查支付状态');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -326,7 +337,11 @@ function Subscription() {
|
|||||||
|
|
||||||
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) {
|
||||||
// 支付成功
|
// 支付成功
|
||||||
@@ -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,
|
||||||
@@ -381,15 +399,20 @@ function Subscription() {
|
|||||||
|
|
||||||
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: '订阅已激活,正在跳转...',
|
||||||
@@ -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}>
|
||||||
|
|||||||
Reference in New Issue
Block a user