1. communityDataSlice 添加事件关注乐观更新 - pending: 立即切换 isFollowing 状态 - rejected: 回滚到之前状态 - fulfilled: 使用 API 返回的准确数据覆盖 2. Mock 数据添加内存状态管理 - 新增 followedEventsSet 和 followedEventsMap 存储 - toggleEventFollowStatus: 切换关注状态 - isEventFollowed: 检查是否已关注 - getFollowedEvents: 获取关注事件列表 3. Mock handlers 使用内存状态 - follow handler: 使用 toggleEventFollowStatus - following handler: 使用 getFollowedEvents 动态返回 - 事件详情: 返回正确的 is_following 状态 修复: 关注事件后导航栏"自选事件"列表不同步更新的问题 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
826 lines
23 KiB
JavaScript
826 lines
23 KiB
JavaScript
// src/mocks/handlers/account.js
|
||
import { http, HttpResponse, delay } from 'msw';
|
||
import { getCurrentUser } from '../data/users';
|
||
import {
|
||
mockWatchlist,
|
||
mockRealtimeQuotes,
|
||
mockFollowingEvents,
|
||
mockEventComments,
|
||
mockInvestmentPlans,
|
||
mockCalendarEvents,
|
||
mockSubscriptionCurrent,
|
||
getCalendarEventsByDateRange,
|
||
getFollowedEvents
|
||
} from '../data/account';
|
||
|
||
// 模拟网络延迟(毫秒)
|
||
const NETWORK_DELAY = 300;
|
||
|
||
export const accountHandlers = [
|
||
// ==================== 用户资料管理 ====================
|
||
|
||
// 1. 获取资料完整度
|
||
http.get('/api/account/profile-completeness', async () => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json({
|
||
success: false,
|
||
error: '用户未登录'
|
||
}, { status: 401 });
|
||
}
|
||
|
||
console.log('[Mock] 获取资料完整度:', currentUser);
|
||
|
||
const isWechatUser = currentUser.has_wechat || !!currentUser.wechat_openid;
|
||
|
||
const completeness = {
|
||
hasPassword: !!currentUser.password_hash || !isWechatUser,
|
||
hasPhone: !!currentUser.phone,
|
||
hasEmail: !!currentUser.email && currentUser.email.includes('@') && !currentUser.email.endsWith('@valuefrontier.temp'),
|
||
isWechatUser: isWechatUser
|
||
};
|
||
|
||
const totalItems = 3;
|
||
const completedItems = [completeness.hasPassword, completeness.hasPhone, completeness.hasEmail].filter(Boolean).length;
|
||
const completenessPercentage = Math.round((completedItems / totalItems) * 100);
|
||
|
||
let needsAttention = false;
|
||
const missingItems = [];
|
||
|
||
if (isWechatUser && completenessPercentage < 100) {
|
||
needsAttention = true;
|
||
if (!completeness.hasPassword) missingItems.push('登录密码');
|
||
if (!completeness.hasPhone) missingItems.push('手机号');
|
||
if (!completeness.hasEmail) missingItems.push('邮箱');
|
||
}
|
||
|
||
const result = {
|
||
success: true,
|
||
data: {
|
||
completeness,
|
||
completenessPercentage,
|
||
needsAttention,
|
||
missingItems,
|
||
isComplete: completedItems === totalItems,
|
||
showReminder: needsAttention
|
||
}
|
||
};
|
||
|
||
console.log('[Mock] 资料完整度结果:', result.data);
|
||
|
||
return HttpResponse.json(result);
|
||
}),
|
||
|
||
// 2. 更新用户资料
|
||
http.put('/api/account/profile', async ({ request }) => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json({
|
||
success: false,
|
||
error: '用户未登录'
|
||
}, { status: 401 });
|
||
}
|
||
|
||
const body = await request.json();
|
||
console.log('[Mock] 更新用户资料:', body);
|
||
|
||
Object.assign(currentUser, body);
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
message: '资料更新成功',
|
||
data: currentUser
|
||
});
|
||
}),
|
||
|
||
// 3. 获取用户资料
|
||
http.get('/api/account/profile', async () => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json({
|
||
success: false,
|
||
error: '用户未登录'
|
||
}, { status: 401 });
|
||
}
|
||
|
||
console.log('[Mock] 获取用户资料:', currentUser);
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: currentUser
|
||
});
|
||
}),
|
||
|
||
// ==================== 自选股管理 ====================
|
||
|
||
// 4. 获取自选股列表
|
||
http.get('/api/account/watchlist', async () => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
// console.log('[Mock] 获取自选股列表'); // 已关闭:减少日志
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: mockWatchlist
|
||
});
|
||
}),
|
||
|
||
// 5. 获取自选股实时行情
|
||
http.get('/api/account/watchlist/realtime', async () => {
|
||
await delay(200);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
console.log('[Mock] 获取自选股实时行情');
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: mockRealtimeQuotes
|
||
});
|
||
}),
|
||
|
||
// 6. 添加自选股
|
||
http.post('/api/account/watchlist', async ({ request }) => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
const body = await request.json();
|
||
const { stock_code, stock_name } = body;
|
||
|
||
console.log('[Mock] 添加自选股:', { stock_code, stock_name });
|
||
|
||
const newItem = {
|
||
id: mockWatchlist.length + 1,
|
||
user_id: currentUser.id,
|
||
stock_code,
|
||
stock_name,
|
||
added_at: new Date().toISOString(),
|
||
industry: '未知',
|
||
current_price: null,
|
||
change_percent: null
|
||
};
|
||
|
||
mockWatchlist.push(newItem);
|
||
|
||
// 同步添加到 mockRealtimeQuotes(导航栏自选股菜单使用此数组)
|
||
mockRealtimeQuotes.push({
|
||
stock_code: stock_code,
|
||
stock_name: stock_name,
|
||
current_price: null,
|
||
change_percent: 0,
|
||
change: 0,
|
||
volume: 0,
|
||
turnover: 0,
|
||
high: 0,
|
||
low: 0,
|
||
open: 0,
|
||
prev_close: 0,
|
||
update_time: new Date().toTimeString().slice(0, 8)
|
||
});
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
message: '添加成功',
|
||
data: newItem
|
||
});
|
||
}),
|
||
|
||
// 7. 删除自选股
|
||
http.delete('/api/account/watchlist/:id', async ({ params }) => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
const { id } = params;
|
||
console.log('[Mock] 删除自选股:', id);
|
||
|
||
// 支持按 stock_code 或 id 匹配删除
|
||
const index = mockWatchlist.findIndex(item =>
|
||
item.stock_code === id || item.id === parseInt(id)
|
||
);
|
||
|
||
if (index !== -1) {
|
||
const stockCode = mockWatchlist[index].stock_code;
|
||
mockWatchlist.splice(index, 1);
|
||
|
||
// 同步从 mockRealtimeQuotes 移除
|
||
const quotesIndex = mockRealtimeQuotes.findIndex(item => item.stock_code === stockCode);
|
||
if (quotesIndex !== -1) {
|
||
mockRealtimeQuotes.splice(quotesIndex, 1);
|
||
}
|
||
}
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
message: '删除成功'
|
||
});
|
||
}),
|
||
|
||
// ==================== 事件关注管理 ====================
|
||
|
||
// 8. 获取关注的事件(使用内存状态动态返回)
|
||
http.get('/api/account/events/following', async () => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
// 从内存存储获取已关注的事件列表
|
||
const followedEvents = getFollowedEvents();
|
||
|
||
console.log('[Mock] 获取关注的事件, 数量:', followedEvents.length);
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: followedEvents
|
||
});
|
||
}),
|
||
|
||
// 9. 获取事件评论
|
||
http.get('/api/account/events/comments', async () => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
console.log('[Mock] 获取事件评论');
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: mockEventComments
|
||
});
|
||
}),
|
||
|
||
// 10. 获取事件帖子(用户发布的评论/帖子)
|
||
http.get('/api/account/events/posts', async () => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
console.log('[Mock] 获取事件帖子');
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: mockEventComments // 复用 mockEventComments 数据
|
||
});
|
||
}),
|
||
|
||
// ==================== 投资计划与复盘 ====================
|
||
|
||
// 10. 获取投资计划列表
|
||
http.get('/api/account/investment-plans', async () => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
console.log('[Mock] 获取投资计划列表');
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: mockInvestmentPlans
|
||
});
|
||
}),
|
||
|
||
// 11. 创建投资计划
|
||
http.post('/api/account/investment-plans', async ({ request }) => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
const body = await request.json();
|
||
console.log('[Mock] 创建投资计划:', body);
|
||
|
||
const newPlan = {
|
||
id: mockInvestmentPlans.length + 301,
|
||
user_id: currentUser.id,
|
||
...body,
|
||
created_at: new Date().toISOString(),
|
||
updated_at: new Date().toISOString()
|
||
};
|
||
|
||
mockInvestmentPlans.push(newPlan);
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
message: '创建成功',
|
||
data: newPlan
|
||
});
|
||
}),
|
||
|
||
// 12. 更新投资计划
|
||
http.put('/api/account/investment-plans/:id', async ({ request, params }) => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
const { id } = params;
|
||
const body = await request.json();
|
||
|
||
console.log('[Mock] 更新投资计划:', { id, body });
|
||
|
||
const index = mockInvestmentPlans.findIndex(plan => plan.id === parseInt(id));
|
||
if (index !== -1) {
|
||
mockInvestmentPlans[index] = {
|
||
...mockInvestmentPlans[index],
|
||
...body,
|
||
updated_at: new Date().toISOString()
|
||
};
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
message: '更新成功',
|
||
data: mockInvestmentPlans[index]
|
||
});
|
||
}
|
||
|
||
return HttpResponse.json({
|
||
success: false,
|
||
error: '计划不存在'
|
||
}, { status: 404 });
|
||
}),
|
||
|
||
// 13. 删除投资计划
|
||
http.delete('/api/account/investment-plans/:id', async ({ params }) => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
const { id } = params;
|
||
console.log('[Mock] 删除投资计划:', id);
|
||
|
||
const index = mockInvestmentPlans.findIndex(plan => plan.id === parseInt(id));
|
||
if (index !== -1) {
|
||
mockInvestmentPlans.splice(index, 1);
|
||
}
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
message: '删除成功'
|
||
});
|
||
}),
|
||
|
||
// ==================== 投资日历 ====================
|
||
|
||
// 14. 获取日历事件(可选日期范围)- 合并投资计划和日历事件
|
||
http.get('/api/account/calendar/events', async ({ request }) => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
// Mock 模式下允许无登录访问,使用默认用户 id: 1
|
||
const currentUser = getCurrentUser() || { id: 1 };
|
||
|
||
const url = new URL(request.url);
|
||
const startDate = url.searchParams.get('start_date');
|
||
const endDate = url.searchParams.get('end_date');
|
||
|
||
console.log('[Mock] 获取日历事件:', { startDate, endDate });
|
||
|
||
// 1. 获取日历事件
|
||
let calendarEvents = mockCalendarEvents;
|
||
if (startDate && endDate) {
|
||
calendarEvents = getCalendarEventsByDateRange(currentUser.id, startDate, endDate);
|
||
}
|
||
|
||
// 2. 获取投资计划和复盘,转换为日历事件格式
|
||
// Mock 模式:不过滤 user_id,显示所有 mock 数据(方便开发测试)
|
||
const investmentPlansAsEvents = mockInvestmentPlans
|
||
.map(plan => ({
|
||
id: plan.id,
|
||
user_id: plan.user_id,
|
||
title: plan.title,
|
||
date: plan.target_date || plan.date,
|
||
event_date: plan.target_date || plan.date,
|
||
type: plan.type, // 'plan' or 'review'
|
||
category: plan.type === 'plan' ? 'investment_plan' : 'investment_review',
|
||
description: plan.content || '',
|
||
importance: 3, // 默认重要度
|
||
source: 'user', // 标记为用户创建
|
||
stocks: plan.stocks || [],
|
||
tags: plan.tags || [],
|
||
status: plan.status,
|
||
created_at: plan.created_at,
|
||
updated_at: plan.updated_at
|
||
}));
|
||
|
||
// 3. 合并两个数据源
|
||
const allEvents = [...calendarEvents, ...investmentPlansAsEvents];
|
||
|
||
// 4. 如果提供了日期范围,对合并后的数据进行过滤
|
||
let filteredEvents = allEvents;
|
||
if (startDate && endDate) {
|
||
const start = new Date(startDate);
|
||
const end = new Date(endDate);
|
||
filteredEvents = allEvents.filter(event => {
|
||
const eventDate = new Date(event.date || event.event_date);
|
||
return eventDate >= start && eventDate <= end;
|
||
});
|
||
}
|
||
|
||
console.log('[Mock] 日历事件详情:', {
|
||
currentUserId: currentUser.id,
|
||
calendarEvents: calendarEvents.length,
|
||
investmentPlansAsEvents: investmentPlansAsEvents.length,
|
||
total: filteredEvents.length,
|
||
plansCount: filteredEvents.filter(e => e.type === 'plan').length,
|
||
reviewsCount: filteredEvents.filter(e => e.type === 'review').length
|
||
});
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: filteredEvents
|
||
});
|
||
}),
|
||
|
||
// 15. 创建日历事件
|
||
http.post('/api/account/calendar/events', async ({ request }) => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
const body = await request.json();
|
||
console.log('[Mock] 创建日历事件:', body);
|
||
|
||
const newEvent = {
|
||
id: mockCalendarEvents.length + 401,
|
||
user_id: currentUser.id,
|
||
...body,
|
||
source: 'user', // 用户创建的事件标记为 'user'
|
||
created_at: new Date().toISOString()
|
||
};
|
||
|
||
mockCalendarEvents.push(newEvent);
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
message: '创建成功',
|
||
data: newEvent
|
||
});
|
||
}),
|
||
|
||
// 16. 更新日历事件
|
||
http.put('/api/account/calendar/events/:id', async ({ request, params }) => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
const { id } = params;
|
||
const body = await request.json();
|
||
|
||
console.log('[Mock] 更新日历事件:', { id, body });
|
||
|
||
const index = mockCalendarEvents.findIndex(event => event.id === parseInt(id));
|
||
if (index !== -1) {
|
||
mockCalendarEvents[index] = {
|
||
...mockCalendarEvents[index],
|
||
...body
|
||
};
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
message: '更新成功',
|
||
data: mockCalendarEvents[index]
|
||
});
|
||
}
|
||
|
||
return HttpResponse.json({
|
||
success: false,
|
||
error: '事件不存在'
|
||
}, { status: 404 });
|
||
}),
|
||
|
||
// 17. 删除日历事件
|
||
http.delete('/api/account/calendar/events/:id', async ({ params }) => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
const { id } = params;
|
||
console.log('[Mock] 删除日历事件:', id);
|
||
|
||
const index = mockCalendarEvents.findIndex(event => event.id === parseInt(id));
|
||
if (index !== -1) {
|
||
mockCalendarEvents.splice(index, 1);
|
||
}
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
message: '删除成功'
|
||
});
|
||
}),
|
||
|
||
// ==================== 订阅信息 ====================
|
||
|
||
// 18. 获取订阅信息
|
||
http.get('/api/subscription/info', async () => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
|
||
if (!currentUser) {
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: {
|
||
type: 'free',
|
||
status: 'active',
|
||
is_active: true,
|
||
days_left: 0,
|
||
end_date: null
|
||
}
|
||
});
|
||
}
|
||
|
||
console.log('[Mock] 获取订阅信息:', currentUser);
|
||
|
||
const subscriptionInfo = {
|
||
type: currentUser.subscription_type || 'free',
|
||
status: currentUser.subscription_status || 'active',
|
||
is_active: currentUser.is_subscription_active !== false,
|
||
days_left: currentUser.subscription_days_left || 0,
|
||
end_date: currentUser.subscription_end_date || null
|
||
};
|
||
|
||
console.log('[Mock] 订阅信息结果:', subscriptionInfo);
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: subscriptionInfo
|
||
});
|
||
}),
|
||
|
||
// 19. 获取当前订阅详情
|
||
http.get('/api/subscription/current', async () => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
if (!currentUser) {
|
||
console.warn('[Mock API] 获取订阅详情失败: 用户未登录');
|
||
return HttpResponse.json(
|
||
{ success: false, error: '未登录' },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
// 基于当前用户的订阅类型返回详情
|
||
const userSubscriptionType = (currentUser.subscription_type || 'free').toLowerCase();
|
||
|
||
const subscriptionDetails = {
|
||
...mockSubscriptionCurrent,
|
||
type: userSubscriptionType,
|
||
status: currentUser.subscription_status || 'active',
|
||
is_active: currentUser.is_subscription_active !== false,
|
||
days_left: currentUser.subscription_days_left || 0,
|
||
end_date: currentUser.subscription_end_date || null
|
||
};
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: subscriptionDetails
|
||
});
|
||
}),
|
||
|
||
// 20. 获取订阅权限
|
||
http.get('/api/subscription/permissions', async () => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const currentUser = getCurrentUser();
|
||
|
||
if (!currentUser) {
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: {
|
||
permissions: {
|
||
'related_stocks': false,
|
||
'related_concepts': false,
|
||
'transmission_chain': false,
|
||
'historical_events': 'limited',
|
||
'concept_html_detail': false,
|
||
'concept_stats_panel': false,
|
||
'concept_related_stocks': false,
|
||
'concept_timeline': false,
|
||
'hot_stocks': false
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
const subscriptionType = (currentUser.subscription_type || 'free').toLowerCase();
|
||
|
||
let permissions = {};
|
||
|
||
if (subscriptionType === 'free') {
|
||
permissions = {
|
||
'related_stocks': false,
|
||
'related_concepts': false,
|
||
'transmission_chain': false,
|
||
'historical_events': 'limited',
|
||
'concept_html_detail': false,
|
||
'concept_stats_panel': false,
|
||
'concept_related_stocks': false,
|
||
'concept_timeline': false,
|
||
'hot_stocks': false
|
||
};
|
||
} else if (subscriptionType === 'pro') {
|
||
permissions = {
|
||
'related_stocks': true,
|
||
'related_concepts': true,
|
||
'transmission_chain': false,
|
||
'historical_events': 'full',
|
||
'concept_html_detail': true,
|
||
'concept_stats_panel': true,
|
||
'concept_related_stocks': true,
|
||
'concept_timeline': false,
|
||
'hot_stocks': true
|
||
};
|
||
} else if (subscriptionType === 'max') {
|
||
permissions = {
|
||
'related_stocks': true,
|
||
'related_concepts': true,
|
||
'transmission_chain': true,
|
||
'historical_events': 'full',
|
||
'concept_html_detail': true,
|
||
'concept_stats_panel': true,
|
||
'concept_related_stocks': true,
|
||
'concept_timeline': true,
|
||
'hot_stocks': true
|
||
};
|
||
}
|
||
|
||
console.log('[Mock] 订阅权限:', { subscriptionType, permissions });
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: {
|
||
subscription_type: subscriptionType,
|
||
permissions
|
||
}
|
||
});
|
||
}),
|
||
|
||
// 21. 获取订阅套餐列表
|
||
http.get('/api/subscription/plans', async () => {
|
||
await delay(NETWORK_DELAY);
|
||
|
||
const plans = [
|
||
{
|
||
id: 1,
|
||
name: 'pro',
|
||
display_name: 'Pro 专业版',
|
||
description: '事件关联股票深度分析 | 历史事件智能对比复盘 | 事件概念关联与挖掘 | 概念板块个股追踪 | 概念深度研报与解读 | 个股异动实时预警',
|
||
monthly_price: 299,
|
||
yearly_price: 2699,
|
||
pricing_options: [
|
||
{ cycle_key: 'monthly', label: '月付', months: 1, price: 299, original_price: null, discount_percent: 0 },
|
||
{ cycle_key: 'quarterly', label: '季付', months: 3, price: 799, original_price: 897, discount_percent: 11 },
|
||
{ cycle_key: 'semiannual', label: '半年付', months: 6, price: 1499, original_price: 1794, discount_percent: 16 },
|
||
{ cycle_key: 'yearly', label: '年付', months: 12, price: 2699, original_price: 3588, discount_percent: 25 }
|
||
],
|
||
features: [
|
||
'新闻信息流',
|
||
'历史事件对比',
|
||
'事件传导链分析(AI)',
|
||
'事件-相关标的分析',
|
||
'相关概念展示',
|
||
'AI复盘功能',
|
||
'企业概览',
|
||
'个股深度分析(AI) - 50家/月',
|
||
'高效数据筛选工具',
|
||
'概念中心(548大概念)',
|
||
'历史时间轴查询 - 100天',
|
||
'涨停板块数据分析',
|
||
'个股涨停分析'
|
||
],
|
||
sort_order: 1
|
||
},
|
||
{
|
||
id: 2,
|
||
name: 'max',
|
||
display_name: 'Max 旗舰版',
|
||
description: '包含Pro版全部功能 | 事件传导链路智能分析 | 概念演变时间轴追溯 | 个股全方位深度研究 | 价小前投研助手无限使用 | 新功能优先体验权 | 专属客服一对一服务',
|
||
monthly_price: 599,
|
||
yearly_price: 5399,
|
||
pricing_options: [
|
||
{ cycle_key: 'monthly', label: '月付', months: 1, price: 599, original_price: null, discount_percent: 0 },
|
||
{ cycle_key: 'quarterly', label: '季付', months: 3, price: 1599, original_price: 1797, discount_percent: 11 },
|
||
{ cycle_key: 'semiannual', label: '半年付', months: 6, price: 2999, original_price: 3594, discount_percent: 17 },
|
||
{ cycle_key: 'yearly', label: '年付', months: 12, price: 5399, original_price: 7188, discount_percent: 25 }
|
||
],
|
||
features: [
|
||
'新闻信息流',
|
||
'历史事件对比',
|
||
'事件传导链分析(AI)',
|
||
'事件-相关标的分析',
|
||
'相关概念展示',
|
||
'板块深度分析(AI)',
|
||
'AI复盘功能',
|
||
'企业概览',
|
||
'个股深度分析(AI) - 无限制',
|
||
'高效数据筛选工具',
|
||
'概念中心(548大概念)',
|
||
'历史时间轴查询 - 无限制',
|
||
'概念高频更新',
|
||
'涨停板块数据分析',
|
||
'个股涨停分析'
|
||
],
|
||
sort_order: 2
|
||
}
|
||
];
|
||
|
||
console.log('[Mock] 获取订阅套餐列表:', plans.length, '个套餐');
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: plans
|
||
});
|
||
}),
|
||
];
|