49 KiB
CLAUDE.md
🌐 语言偏好: 请始终使用中文与用户交流,包括所有解释、分析、文档编写和代码注释。
本文件为 Claude Code (claude.ai/code) 提供在此代码库中工作的指导说明。
📖 文档结构导航
本文档分为以下主要章节,建议按需查阅:
基础信息:
架构设计:
开发指南:
- 开发工作流 - 路由、组件、API、Redux 开发指南
- TypeScript 接入 - TypeScript 渐进式迁移方案与指南
- 常见开发任务 - 5 个详细的开发任务教程
- 技术路径与开发指南 - UI 框架选型、技术栈演进、最佳实践
技术决策与规范:
- UI 框架选型与使用指南 - Ant Design + Chakra UI 混合策略
- 架构设计原则 - 组件设计、状态管理、代码分割、API 层
- 开发规范与最佳实践 - 命名规范、Git 工作流、代码审查
技术债务与维护:
项目概览
混合式 React 仪表板,用于金融/交易分析,采用 Flask 后端。基于 Argon Dashboard Chakra PRO 模板构建。
技术栈
前端
- 核心框架: React 18.3.1
- 类型系统: TypeScript 5.9.3(渐进式接入中,支持 JS/TS 混合开发)
- UI 组件库: Chakra UI 2.8.2(主要) + Ant Design 5.27.4(表格/表单)
- 状态管理: Redux Toolkit 2.9.2
- 路由: React Router v6.30.1 配合 React.lazy() 实现代码分割
- 构建系统: CRACO 7.1.0 + 激进的 webpack 5 优化
- 图表库: ECharts 5.6.0、ApexCharts 3.27.3、Recharts 3.1.2、D3 7.9.0、Visx 3.12.0
- 动画: Framer Motion 4.1.17
- 日历: FullCalendar 5.9.0、React Big Calendar 0.33.2
- 图标: Lucide React 0.540.0(推荐)、React Icons 4.12.0、@ant-design/icons 6.0.0
- 网络请求: Axios 1.10.0
- 实时通信: Socket.IO Client 4.7.4
- 数据分析: PostHog 1.281.0
- 开发工具: MSW (Mock Service Worker) 用于 API mocking
- 虚拟化: @tanstack/react-virtual 3.13.12(性能优化)
- 其他: Draft.js(富文本编辑)、React Markdown、React Quill
后端
- Flask + SQLAlchemy ORM
- ClickHouse(分析型数据库)+ MySQL/PostgreSQL(事务型数据库)
- Flask-SocketIO 实现 WebSocket 实时更新
- Celery + Redis 处理后台任务
- 腾讯云短信 + 微信支付集成
开发命令
前端开发
npm start # 使用 mock 数据启动(.env.mock),代理到 localhost:5001
npm run start:real # 使用真实后端启动(.env.local)
npm run start:dev # 使用开发配置启动(.env.development)
npm run start:test # 同时启动后端(app.py)和前端(.env.test)
npm run dev # 'npm start' 的别名
npm run backend # 仅启动 Flask 服务器(python app.py)
npm run build # 生产环境构建,包含 Gulp 许可证头
npm run build:analyze # 使用 webpack bundle analyzer 构建
npm test # 运行 React 测试套件(CRACO)
npm run lint:check # 检查 ESLint 规则(退出码 0)
npm run lint:fix # 自动修复 ESLint 问题
npm run type-check # TypeScript 类型检查(不生成输出)
npm run type-check:watch # TypeScript 类型检查监听模式
npm run clean # 删除 node_modules 和 package-lock.json
npm run reinstall # 清洁安装(运行 clean + install)
后端开发
python app.py # 主 Flask 服务器
python simulation_background_processor.py # 交易模拟的后台任务处理器
pip install -r requirements.txt # 安装 Python 依赖
部署
npm run deploy # 从本地部署(scripts/deploy-from-local.sh)
npm run rollback # 回滚到上一个版本
架构
应用入口流程
src/index.js
└── src/App.js(根组件)
├── AppProviders (src/providers/AppProviders.js)
│ ├── ReduxProvider(store 来自 src/store/)
│ ├── ChakraProvider(主题来自 src/theme/)
│ ├── NotificationProvider (src/contexts/NotificationContext.js)
│ └── AuthProvider (src/contexts/AuthContext.js)
├── AppRoutes (src/routes/index.js)
│ ├── MainLayout 路由(带导航栏/页脚)
│ └── 独立路由(认证页面、全屏视图)
└── GlobalComponents(模态框覆盖层、全局 UI)
路由架构(模块化设计)
路由采用声明式设计,并拆分到 src/routes/ 中的多个文件:
- index.js - 主路由器(组合配置 + 渲染路由)
- routeConfig.js - 路由定义(路径、组件、保护模式、布局、子路由)
- lazy-components.js - React.lazy() 导入,用于代码分割
- homeRoutes.js - 嵌套的首页路由
- constants/ - 保护模式、布局映射
- utils/ - 路由渲染逻辑(wrapWithProtection、renderRoute)
路由保护模式(PROTECTION_MODES):
PUBLIC- 无需认证MODAL- 未登录时显示认证模态框REDIRECT- 未登录时重定向到 /auth/sign-in
前端目录结构详解
本项目采用功能驱动的目录结构,按职责将代码组织到不同目录中。以下是完整的目录结构及详细说明:
核心目录概览
src/
├── index.js # 应用入口文件
├── App.js # 根组件(组合 Providers + Routes)
│
├── providers/ # Provider 组合层
├── routes/ # 路由配置与管理
├── layouts/ # 页面布局模板
├── views/ # 页面级组件
├── components/ # 可复用 UI 组件
│
├── contexts/ # React Context 状态管理
├── store/ # Redux 全局状态管理
├── hooks/ # 自定义 React Hooks
│
├── services/ # API 服务层
├── utils/ # 工具函数库
├── constants/ # 全局常量定义
│
├── theme/ # UI 主题配置
├── assets/ # 静态资源
├── styles/ # 全局样式
│
├── mocks/ # 开发环境 Mock 数据
├── lib/ # 第三方库配置
└── variables/ # 可配置变量
详细目录说明
📁 src/providers/ - Provider 组合层
用途: 集中管理应用的所有 Context Providers,避免在 App.js 中嵌套过深。
核心文件:
AppProviders.js- 所有 Provider 的组合容器
Provider 嵌套顺序(从外到内):
ReduxProvider // 1. Redux 状态管理(最外层)
└─ ChakraProvider // 2. Chakra UI 主题系统
└─ ConfigProvider // 3. Ant Design 主题配置
└─ NotificationProvider // 4. 通知系统
└─ AuthProvider // 5. 认证系统(最内层)
何时修改:
- 添加新的全局 Provider(如 i18n、Analytics)
- 调整 Provider 顺序(注意依赖关系)
📁 src/routes/ - 路由系统
用途: 模块化路由配置,采用声明式设计,支持懒加载和路由保护。
核心文件:
index.js- 主路由器(渲染所有路由)routeConfig.js- 路由配置中心(所有路由定义)lazy-components.js- React.lazy() 懒加载组件导入homeRoutes.js- 首页嵌套路由constants/protectionModes.js- 路由保护模式定义utils/wrapWithProtection.js- 路由保护逻辑utils/renderRoute.js- 路由渲染工具
路由保护模式:
PUBLIC- 公开访问,无需登录MODAL- 未登录时显示登录模态框(不跳转)REDIRECT- 未登录时重定向到 /auth/sign-in
添加新路由的步骤: 参见"常见开发任务 → 如何添加新的页面路由"
命名约定:
- 路由路径使用 kebab-case:
portfolio,trading-simulation - 组件名使用 PascalCase:
Portfolio,TradingSimulation
📁 src/layouts/ - 页面布局
用途: 定义页面的通用布局结构(导航栏、侧边栏、页脚等),复用于多个页面。
核心文件:
MainLayout.js- 主布局(带顶部导航栏 + 侧边栏 + 页脚)Auth.js- 认证页面布局(无导航栏,纯净背景)
布局特性:
- 每个布局包含独立的 ErrorBoundary(错误隔离)
- 通过
<Outlet />渲染子路由内容 - 布局内可访问认证状态(AuthContext)
何时创建新布局:
- 需要完全不同的页面结构(如打印页、全屏图表)
- 特定页面需要自定义导航栏/侧边栏
📁 src/views/ - 页面级组件
用途: 存放路由对应的页面组件,每个文件对应一个路由页面。
组织结构:
views/
├── Community/ # 社区页面(大型功能模块)
│ ├── index.js # 页面入口
│ ├── components/ # 页面专属组件
│ │ ├── EventList/
│ │ ├── EventCard/
│ │ └── StockDetailPanel/
│ ├── hooks/ # 页面专属 Hooks
│ │ └── useCommunityData.js
│ └── utils/ # 页面专属工具函数
│
├── TradingSimulation/ # 交易模拟页面
│ ├── index.js
│ ├── components/
│ └── ...
│
├── Dashboard/ # 仪表板页面
│ └── index.js
│
└── Portfolio/ # 投资组合页面(简单页面)
└── index.js
命名约定:
- 目录名使用 PascalCase:
Community,TradingSimulation - 主文件始终为
index.js(方便导入)
原则:
- 页面组件主要负责数据获取和布局组合,不应包含复杂的 UI 逻辑
- 复杂 UI 逻辑应拆分到
components/子目录 - 页面超过 500 行时考虑拆分(参见"组件组织模式")
📁 src/components/ - 可复用 UI 组件
用途: 存放跨页面复用的通用 UI 组件(按钮、卡片、表格、模态框等)。
组织结构(推荐采用原子设计模式):
components/
├── Atoms/ # 原子组件(1-50 行)
│ ├── Button/
│ ├── Badge/
│ └── Icon/
│
├── Molecules/ # 分子组件(50-150 行)
│ ├── Card/
│ ├── StatCard/
│ └── SearchBar/
│
├── Organisms/ # 有机体组件(150-500 行)
│ ├── Navbar/
│ ├── Sidebar/
│ ├── DataTable/
│ └── FilterPanel/
│
└── Templates/ # 模板组件(可选)
└── PageTemplate/
何时添加新组件:
- 组件在 2+ 个页面中使用
- 组件具有明确的职责和边界
- 组件可独立测试
命名约定:
- 组件目录使用 PascalCase:
EventCard,StockTable - 每个组件目录包含:
index.js- 主导出文件ComponentName.js- 实现代码(可选,简单组件直接在 index.js)ComponentName.test.js- 测试文件(可选)
避免:
- 将页面特定组件放在这里(应放在
views/{PageName}/components/) - 过度拆分(不要为了拆分而拆分)
📁 src/contexts/ - React Context 状态管理
用途: 存放使用 React Context API 管理的跨组件状态。
核心 Contexts:
AuthContext.js- 认证状态(用户信息、登录状态、登录/登出方法)NotificationContext.js- 通知系统(显示 Toast/Alert)SidebarContext.js- 侧边栏状态(展开/收起)
何时使用 Context:
- 跨层级传递数据(避免 props drilling)
- 不频繁变化的数据(主题、语言、认证状态)
- 依赖注入场景
何时不使用 Context:
- 频繁变化的数据 → 使用 Redux
- 服务端数据 → 使用 React Query(计划中)
- 本地 UI 状态 → 使用 useState
Context 文件结构:
// AuthContext.js
export const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
// ...
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => useContext(AuthContext); // 自定义 Hook
📁 src/store/ - Redux 全局状态管理
用途: 使用 Redux Toolkit 管理全局状态(UI 状态、跨页面共享数据)。
目录结构:
store/
├── index.js # Store 配置(combineReducers)
└── slices/ # Redux Slices
├── authModalSlice.js # 认证模态框状态
├── posthogSlice.js # PostHog 分析配置
├── stockSlice.js # 股票数据
├── industrySlice.js # 行业/概念数据
├── subscriptionSlice.js # 用户订阅状态
└── communityDataSlice.js # 社区数据
何时使用 Redux:
- 全局 UI 状态(模态框开关、侧边栏状态)
- 需要在多个不相关组件间共享的数据
- 需要持久化的状态(与 redux-persist 结合)
- 需要中间件处理的复杂逻辑
Slice 命名约定:
- 文件名:
{feature}Slice.js(如authModalSlice.js) - Slice 名称:
{feature}(如name: 'authModal') - Actions: 动词开头 (如
openModal,closeModal,setUser)
添加新 Slice: 参见"常见开发任务 → 如何添加新的 Redux Slice"
📁 src/hooks/ - 自定义 React Hooks
用途: 存放可复用的自定义 Hooks,封装常见逻辑模式。
Hook 类型示例:
hooks/
├── useAuth.js # 认证相关(实际导出自 AuthContext)
├── useStockData.js # 数据获取 Hook
├── useDebounce.js # 防抖 Hook
├── useIntersectionObserver.js # 懒加载/无限滚动
├── useLocalStorage.js # 本地存储持久化
├── useMediaQuery.js # 响应式查询
└── usePrevious.js # 获取前一个值
命名约定:
- 文件名:
use{PascalCase}.js(如useStockData.js) - 必须以
use开头(React Hooks 规则) - 一个文件导出一个 Hook
Hook 设计原则:
- 职责单一(只做一件事)
- 返回对象或数组(不返回原始值)
- 包含完整的状态管理(loading, error, data)
何时创建新 Hook:
- 逻辑在 2+ 个组件中重复
- 组件逻辑可以独立测试
- 需要封装浏览器 API(localStorage, IntersectionObserver)
📁 src/services/ - API 服务层
用途: 封装所有后端 API 调用,提供统一的接口供组件调用。
组织结构(按业务领域划分):
services/
├── authService.js # 认证相关 API(登录、注册、登出)
├── stockService.js # 股票数据 API
├── portfolioService.js # 投资组合 API
├── communityService.js # 社区内容 API
├── tradingService.js # 交易模拟 API
└── userService.js # 用户信息 API
服务函数命名约定:
fetch{Entity}- 获取数据 (如fetchStockData)create{Entity}- 创建记录 (如createOrder)update{Entity}- 更新记录 (如updateProfile)delete{Entity}- 删除记录 (如deletePost)
标准 API 调用结构:
// stockService.js
import axios from 'axios';
import { getApiBase } from '@utils/apiConfig';
const api = axios.create({
baseURL: getApiBase(),
timeout: 10000,
});
export const fetchStockData = async (stockCode) => {
try {
const response = await api.get(`/api/stocks/${stockCode}`);
return response.data;
} catch (error) {
console.error('fetchStockData error:', error);
throw error;
}
};
原则:
- 组件不应直接调用 axios(通过 service 层)
- 每个 service 函数应包含错误处理
- 使用
getApiBase()获取 API 基础 URL(支持 Mock 模式)
📁 src/utils/ - 工具函数库
用途: 存放纯函数工具、格式化工具、计算逻辑等不依赖 React 的通用代码。
核心工具文件:
utils/
├── apiConfig.js # API 配置(getApiBase, isMockMode)
├── priceFormatters.js # 价格/数字格式化函数
├── dateFormatters.js # 日期格式化函数
├── logger.js # 统一日志工具
├── validators.js # 表单验证函数
├── calculations.js # 金融计算函数
└── string.js # 字符串处理函数
命名约定:
- 文件名:
camelCase.js(如priceFormatters.js) - 函数名:
camelCase(如formatPrice,calculateProfit) - 导出方式: 命名导出 (如
export const formatPrice = ...)
工具函数特点:
- 纯函数: 相同输入始终返回相同输出,无副作用
- 可测试: 易于编写单元测试
- 可复用: 多处使用的逻辑
示例:
// priceFormatters.js
export const formatPrice = (price, currency = '¥') => {
return `${currency}${price.toFixed(2)}`;
};
export const formatPercent = (value, decimals = 2) => {
return `${(value * 100).toFixed(decimals)}%`;
};
📁 src/constants/ - 全局常量定义
用途: 存放全局常量、枚举值、配置对象等不可变数据。
常见常量文件:
constants/
├── animations.js # 动画配置(duration、easing)
├── routes.js # 路由路径常量
├── apiEndpoints.js # API 端点常量
├── colors.js # 颜色常量(补充主题)
├── tradingConfig.js # 交易相关配置
└── errorMessages.js # 错误消息常量
命名约定:
- 常量名使用 SCREAMING_SNAKE_CASE:
API_BASE_URL,MAX_RETRY_COUNT - 配置对象使用 camelCase:
animationConfig,chartColors
示例:
// animations.js
export const ANIMATION_DURATION = {
FAST: 150,
NORMAL: 300,
SLOW: 500,
};
export const EASING = {
EASE_IN_OUT: 'cubic-bezier(0.4, 0, 0.2, 1)',
EASE_OUT: 'cubic-bezier(0, 0, 0.2, 1)',
};
原则:
- 避免魔法数字/字符串(在代码中硬编码)
- 方便统一修改(单一数据源)
- 提高代码可读性
📁 src/theme/ - UI 主题配置
用途: 定义 Chakra UI 主题配置(颜色、字体、间距、组件样式等)。
核心文件:
theme/
├── theme.js # 主题主文件(导出完整主题)
├── foundations/ # 基础样式(颜色、字体、间距)
│ ├── colors.js
│ ├── typography.js
│ └── spacing.js
└── components/ # 组件级主题覆盖
├── button.js
├── card.js
└── modal.js
主题配置示例:
// theme.js
import { extendTheme } from '@chakra-ui/react';
import { colors } from './foundations/colors';
import { ButtonStyles as Button } from './components/button';
const theme = extendTheme({
colors,
fonts: {
heading: `'Open Sans', sans-serif`,
body: `'Raleway', sans-serif`,
},
components: {
Button,
},
});
export default theme;
何时修改主题:
- 添加自定义颜色/渐变
- 统一修改所有 Button/Card 的默认样式
- 配置深色模式
📁 src/assets/ - 静态资源
用途: 存放图片、字体、图标等静态文件。
组织结构:
assets/
├── img/ # 图片资源
│ ├── logo.png
│ ├── backgrounds/
│ └── illustrations/
├── icons/ # SVG 图标
└── fonts/ # 自定义字体文件(如需要)
使用方式:
import logo from '@assets/img/logo.png';
<img src={logo} alt="Logo" />
📁 src/mocks/ - 开发环境 Mock 数据
用途: 使用 MSW (Mock Service Worker) 拦截 API 请求,返回 mock 数据,实现前后端分离开发。
目录结构:
mocks/
├── browser.js # MSW 初始化(浏览器环境)
├── handlers/ # API Mock Handlers(按领域划分)
│ ├── index.js # 汇总所有 handlers
│ ├── auth.js # 认证相关 API mock
│ ├── stock.js # 股票数据 API mock
│ ├── portfolio.js # 投资组合 API mock
│ └── community.js # 社区内容 API mock
└── data/ # Mock 数据文件
├── stocks.json
├── users.json
└── events.json
启用 Mock 模式:
# 方式 1: 使用 .env.mock 配置(默认)
npm start
# 方式 2: 手动设置环境变量
REACT_APP_ENABLE_MOCK=true npm start
添加新 Mock Handler: 参见"常见开发任务 → 如何添加新的 MSW Mock Handler"
原则:
- Mock 数据应尽量模拟真实数据结构
- 使用 MSW 的
http.get/post/put/delete定义 handlers - Handler 中可模拟延迟、错误响应(测试加载/错误状态)
📁 其他目录
src/lib/ - 第三方库配置
- 存放第三方库的自定义配置(如 PostHog、Analytics)
src/styles/ - 全局样式
- 存放全局 CSS/SCSS 文件(如 reset.css、global.scss)
src/variables/ - 可配置变量
- 存放可通过环境变量覆盖的配置(如 API URLs、Feature Flags)
目录结构最佳实践
1. 就近原则(Co-location)
- 将相关文件放在一起(组件、样式、测试、工具函数)
- 示例:
EventCard/目录包含EventCard.js、EventCard.test.js、utils.js
2. 单一职责
- 每个目录只负责一个领域(不要混合业务逻辑和 UI 组件)
- 示例:
services/只包含 API 调用,不包含 UI 组件
3. 避免深层嵌套
- 目录层级不超过 4 层(超过则考虑重构)
- 示例:
src/views/Community/components/EventCard/已经是 4 层
4. 命名一致性
- 组件: PascalCase (
EventCard) - 文件: camelCase (
priceFormatters.js) - 常量: SCREAMING_SNAKE_CASE (
API_BASE_URL)
5. 导出规范
- 每个目录包含
index.js作为主导出文件 - 使用命名导出 (
export const ...) 而非默认导出(除组件外)
查找文件指南
如何快速找到想要修改的文件?
| 需求 | 目录 | 示例 |
|---|---|---|
| 修改页面布局 | src/layouts/ |
MainLayout.js |
| 修改某个页面 | src/views/{PageName}/ |
Community/index.js |
| 修改可复用组件 | src/components/ |
DataTable/index.js |
| 修改 API 调用 | src/services/ |
stockService.js |
| 修改工具函数 | src/utils/ |
priceFormatters.js |
| 修改全局状态 | src/store/slices/ |
stockSlice.js |
| 添加新路由 | src/routes/routeConfig.js |
- |
| 修改主题颜色 | src/theme/foundations/colors.js |
- |
| 添加 Mock API | src/mocks/handlers/ |
stock.js |
使用路径别名快速导入:
// ✅ 使用别名(推荐)
import { EventCard } from '@components/EventCard';
import { formatPrice } from '@utils/priceFormatters';
import { fetchStockData } from '@services/stockService';
// ❌ 使用相对路径(不推荐)
import { EventCard } from '../../../components/EventCard';
import { formatPrice } from '../../utils/priceFormatters';
后端架构详解
本项目后端采用 Flask 微服务架构,结合多种数据库和消息队列,实现高性能金融数据处理和实时通信。
后端目录结构
项目根目录/
├── app.py # 主 Flask 应用(Web 服务器 + API 路由)
├── simulation_background_processor.py # Celery 后台处理器(交易模拟任务)
├── concept_api.py # 概念/行业分析独立 API 服务
│
├── wechat_pay.py # 微信支付业务逻辑
├── wechat_pay_config.py # 微信支付配置(商户号、密钥等)
│
├── tdays.csv # 交易日历数据(A 股交易日)
├── requirements.txt # Python 依赖包清单
│
├── models/ # 数据库模型定义(SQLAlchemy)
│ ├── user.py
│ ├── stock.py
│ └── transaction.py
│
├── services/ # 业务逻辑服务层
│ ├── stock_service.py
│ ├── trading_service.py
│ └── notification_service.py
│
├── utils/ # 后端工具函数
│ ├── db_utils.py # 数据库连接工具
│ ├── clickhouse_client.py # ClickHouse 客户端封装
│ └── date_utils.py # 日期处理工具
│
└── config/ # 配置文件
├── development.py
├── production.py
└── test.py
核心组件详解
1️⃣ app.py - 主 Flask 应用
职责: Web 服务器、API 路由、认证、会话管理、WebSocket 通信
核心功能:
- Flask 应用初始化: 配置 CORS、Session、Logging
- API 路由定义: RESTful API 端点(
/api/stocks,/api/portfolio,/api/community) - 认证系统: Flask-Login 集成(登录、登出、权限校验)
- WebSocket 服务: Flask-SocketIO 实现实时推送(股票行情、事件通知)
- 数据库连接: SQLAlchemy ORM + ClickHouse Client
- 交易日历加载: 启动时从
tdays.csv加载 A 股交易日数据到全局变量
技术栈:
- Flask - 轻量级 Web 框架
- Flask-Login - 用户会话管理
- Flask-SocketIO - WebSocket 实时通信
- Flask-CORS - 跨域资源共享配置
- SQLAlchemy - ORM(对象关系映射)
API 路由示例:
# 获取股票数据
@app.route('/api/stocks/<stock_code>', methods=['GET'])
@login_required
def get_stock_data(stock_code):
# 从 ClickHouse 查询历史数据
data = clickhouse_client.query(f"SELECT * FROM stocks WHERE code = '{stock_code}'")
return jsonify(data)
# 创建交易订单
@app.route('/api/orders', methods=['POST'])
@login_required
def create_order():
data = request.json
order = Order(user_id=current_user.id, stock_code=data['stock_code'], ...)
db.session.add(order)
db.session.commit()
return jsonify({'order_id': order.id})
WebSocket 事件示例:
# 客户端订阅股票行情
@socketio.on('subscribe_stock')
def handle_subscribe(data):
stock_code = data['stock_code']
join_room(f'stock_{stock_code}')
emit('subscribed', {'stock_code': stock_code})
# 服务端推送行情更新
def push_quote_update(stock_code, quote_data):
socketio.emit('stock_quote', quote_data, room=f'stock_{stock_code}')
交易日历加载:
# 全局变量存储交易日
trading_days = []
# 启动时加载 tdays.csv
def load_trading_days():
global trading_days
with open('tdays.csv', 'r') as f:
trading_days = [line.strip() for line in f.readlines()]
# 判断是否为交易日
def is_trading_day(date_str):
return date_str in trading_days
2️⃣ simulation_background_processor.py - Celery 后台处理器
职责: 处理长时间运行的后台任务(交易模拟、报表生成、数据同步)
为什么需要后台处理器?
- 交易模拟需要执行数百次历史回测计算(耗时 10-60 秒)
- 避免阻塞 Flask 主线程(影响其他 API 响应)
- 支持任务队列、重试、失败处理
技术栈:
- Celery - 分布式任务队列
- Redis - 消息代理(Broker)和结果存储(Backend)
- ClickHouse - 高性能查询历史股票数据
任务示例:
from celery import Celery
# Celery 实例
celery_app = Celery('tasks', broker='redis://localhost:6379/0')
# 交易模拟任务
@celery_app.task(bind=True)
def run_simulation(self, user_id, strategy_config):
"""
执行交易模拟任务
:param user_id: 用户 ID
:param strategy_config: 策略配置(买入条件、卖出条件、资金管理)
:return: 模拟结果(收益率、最大回撤、交易明细)
"""
try:
# 1. 从 ClickHouse 获取历史数据
stock_data = clickhouse_client.query(...)
# 2. 执行回测计算
for i in range(len(stock_data)):
# 模拟交易逻辑
if should_buy(stock_data[i]):
buy(stock_data[i])
if should_sell(stock_data[i]):
sell(stock_data[i])
# 3. 计算绩效指标
result = {
'total_return': calculate_return(),
'max_drawdown': calculate_drawdown(),
'trades': get_trade_history(),
}
# 4. 保存结果到 MySQL
save_simulation_result(user_id, result)
return result
except Exception as e:
# 失败重试(最多 3 次)
self.retry(exc=e, countdown=60, max_retries=3)
从 Flask 调用 Celery 任务:
# app.py
@app.route('/api/simulations', methods=['POST'])
@login_required
def start_simulation():
strategy = request.json
# 异步提交任务到 Celery
task = run_simulation.delay(current_user.id, strategy)
return jsonify({
'task_id': task.id,
'status': 'pending',
'message': '模拟任务已提交,请稍后查看结果'
})
# 查询任务状态
@app.route('/api/simulations/<task_id>', methods=['GET'])
def get_simulation_status(task_id):
task = run_simulation.AsyncResult(task_id)
if task.state == 'PENDING':
response = {'status': 'pending', 'progress': 0}
elif task.state == 'SUCCESS':
response = {'status': 'completed', 'result': task.result}
elif task.state == 'FAILURE':
response = {'status': 'failed', 'error': str(task.info)}
return jsonify(response)
启动 Celery Worker:
# 启动 worker(监听任务队列)
celery -A simulation_background_processor worker --loglevel=info
# 启动 Flower(Celery 监控工具)
celery -A simulation_background_processor flower
3️⃣ concept_api.py - 概念/行业分析 API
职责: 独立的概念板块分析服务(涨停分析、热点行业、资金流向)
为什么独立部署?
- 概念分析计算密集(需要遍历数千只股票)
- 独立扩展性(可部署到多台服务器)
- 服务隔离(不影响主应用性能)
部署架构:
前端请求 → Nginx 反向代理
├─ /api → Flask 主应用 (端口 5001)
└─ /concept-api → Concept API (端口 6801)
API 示例:
from flask import Flask, jsonify
import clickhouse_driver
app = Flask(__name__)
# 获取涨停股票列表
@app.route('/limit_up', methods=['GET'])
def get_limit_up_stocks():
"""
获取当日涨停股票列表,按概念板块分组
"""
query = """
SELECT
stock_code,
stock_name,
concept,
close_price,
change_percent
FROM stock_daily
WHERE trade_date = today()
AND change_percent >= 9.9
ORDER BY concept, change_percent DESC
"""
data = clickhouse_client.query(query)
# 按概念分组
result = {}
for row in data:
concept = row['concept']
if concept not in result:
result[concept] = []
result[concept].append(row)
return jsonify(result)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=6801)
前端调用(通过代理):
// craco.config.js 配置代理
proxy: {
'/concept-api': {
target: 'http://49.232.185.254:6801',
pathRewrite: { '^/concept-api': '' },
}
}
// 前端调用
fetch('/concept-api/limit_up')
.then(res => res.json())
.then(data => console.log(data));
4️⃣ 微信支付集成
文件:
wechat_pay.py- 微信支付业务逻辑(下单、查询、退款)wechat_pay_config.py- 配置(商户号、API 密钥、证书路径)
核心功能:
- 统一下单: 创建支付订单(JSAPI、Native、H5)
- 支付回调: 接收微信支付结果通知
- 订单查询: 查询订单支付状态
- 退款处理: 发起退款请求
示例代码:
# wechat_pay.py
from wechatpy.pay import WeChatPay
from wechat_pay_config import MCHID, API_KEY, CERT_PATH
# 初始化微信支付客户端
wechat_pay = WeChatPay(
appid='your_appid',
api_key=API_KEY,
mch_id=MCHID,
mch_cert=CERT_PATH,
)
# 创建订单
def create_order(user_id, amount, description):
result = wechat_pay.order.create(
trade_type='JSAPI',
body=description,
total_fee=int(amount * 100), # 单位:分
notify_url='https://yourdomain.com/api/wechat/callback',
user_id=user_id,
)
return result
# 支付回调处理
@app.route('/api/wechat/callback', methods=['POST'])
def wechat_callback():
xml_data = request.data
data = wechat_pay.parse_payment_result(xml_data)
if data['return_code'] == 'SUCCESS':
# 更新订单状态
order_id = data['out_trade_no']
update_order_status(order_id, 'paid')
return wechat_pay.reply('OK', True)
5️⃣ 数据库架构
多数据库策略:
| 数据库 | 用途 | 特点 | 示例表 |
|---|---|---|---|
| ClickHouse | 时序数据存储 | OLAP(列式存储、查询速度快) | stock_daily(日线数据)stock_minute(分钟数据)concept_daily(概念板块数据) |
| MySQL/PostgreSQL | 事务数据存储 | OLTP(ACID 保证、关系型) | users(用户表)orders(订单表)portfolios(持仓表)subscriptions(订阅表) |
| Redis | 缓存 + 消息队列 | 内存数据库(高速读写) | 股票行情缓存 用户 Session Celery 任务队列 |
ClickHouse 使用场景:
# 查询某只股票的历史数据(100 万行数据,查询耗时 < 100ms)
query = """
SELECT
trade_date,
open, high, low, close, volume
FROM stock_daily
WHERE stock_code = '600000.SH'
AND trade_date >= '2020-01-01'
ORDER BY trade_date
"""
data = clickhouse_client.query(query)
MySQL 使用场景:
# 创建订单(事务保证)
@app.route('/api/orders', methods=['POST'])
def create_order():
try:
# 开启事务
order = Order(user_id=user_id, stock_code=stock_code, ...)
db.session.add(order)
# 扣减账户余额
account = Account.query.get(user_id)
account.balance -= order.total_amount
# 提交事务
db.session.commit()
return jsonify({'success': True})
except Exception as e:
# 回滚事务
db.session.rollback()
return jsonify({'error': str(e)}), 500
后端技术栈详解
核心框架:
- Flask 2.x - 轻量级 Web 框架,易于扩展
- Flask-Login - 用户认证与会话管理
- Flask-SocketIO - WebSocket 实时通信(基于 Socket.IO 协议)
- Flask-CORS - 跨域资源共享配置
数据库与 ORM:
- SQLAlchemy - Python ORM,支持 MySQL/PostgreSQL
- ClickHouse Driver - ClickHouse Python 客户端
- Redis-py - Redis Python 客户端
后台任务处理:
- Celery - 分布式任务队列
- Redis - Celery 消息代理(Broker)
第三方服务:
- 微信支付 SDK -
wechatpy - 腾讯云短信 SDK - 发送验证码、通知
后端开发最佳实践
1. API 设计规范:
# ✅ RESTful 风格
GET /api/stocks # 获取股票列表
GET /api/stocks/:id # 获取单个股票
POST /api/stocks # 创建股票
PUT /api/stocks/:id # 更新股票
DELETE /api/stocks/:id # 删除股票
# ✅ 统一响应格式
{
"code": 200,
"message": "success",
"data": { ... }
}
# ❌ 避免使用动词命名
POST /api/getStockData # 不推荐
POST /api/createNewOrder # 不推荐
2. 错误处理:
# 统一错误处理
@app.errorhandler(Exception)
def handle_error(e):
if isinstance(e, ValidationError):
return jsonify({'error': str(e)}), 400
elif isinstance(e, Unauthorized):
return jsonify({'error': 'Unauthorized'}), 401
else:
logger.error(f'Unhandled exception: {e}')
return jsonify({'error': 'Internal server error'}), 500
3. 数据库连接管理:
# 使用连接池
engine = create_engine(
'mysql://user:pass@localhost/db',
pool_size=10,
max_overflow=20,
pool_recycle=3600,
)
# 请求结束后自动关闭连接
@app.teardown_appcontext
def shutdown_session(exception=None):
db.session.remove()
4. 性能优化:
- 数据库索引: 为常用查询字段添加索引
- 查询优化: 避免 N+1 查询,使用
joinedload - 缓存策略: 使用 Redis 缓存热点数据(股票行情、用户信息)
- 异步任务: 耗时操作使用 Celery 异步处理
后端部署架构
生产环境部署:
Nginx (反向代理 + 负载均衡)
├─ Gunicorn (WSGI 服务器) × 4 进程
│ └─ Flask 应用 (app.py)
│
├─ Celery Worker × 2 进程
│ └─ 后台任务处理
│
├─ Redis (缓存 + 消息队列)
├─ MySQL (事务数据)
└─ ClickHouse (时序数据)
启动命令:
# 启动 Flask 应用(Gunicorn)
gunicorn -w 4 -b 0.0.0.0:5001 app:app
# 启动 Celery Worker
celery -A simulation_background_processor worker --loglevel=info
# 启动 Concept API
python concept_api.py
监控与日志:
- 日志: 使用 Python
logging模块记录日志 - 监控: Celery Flower 监控任务状态
- 性能: 使用 APM 工具(如 New Relic、Datadog)
配置
环境文件
.env.mock - Mock 模式(默认):MSW 拦截所有 API 调用,无需后端
.env.development - 开发模式:连接到开发后端
.env.test - 测试模式:用于 'npm run start:test'(后端 + 前端一起)
.env.production - 生产环境构建配置
关键环境变量:
REACT_APP_ENABLE_MOCK=true- 启用 MSW mockingREACT_APP_API_URL- 后端 URL(空字符串 = 使用相对路径或 MSW)
MSW (Mock Service Worker) 设置
MSW 用于开发期间的 API mocking:
- 激活方式:在 env 文件中设置
REACT_APP_ENABLE_MOCK=true - Worker 文件:
public/mockServiceWorker.js(自动生成) - Handlers:
src/mocks/handlers/(按领域组织:auth、stock、company 等) - 模式:
onUnhandledRequest: 'warn'- 未处理的请求会传递到后端
当 MSW 激活时,开发服务器代理被禁用(MSW 优先拦截)。
路径别名 (craco.config.js)
所有别名解析到 src/ 子目录:
@/ → src/
@assets/ → src/assets/
@components/ → src/components/
@constants/ → src/constants/
@contexts/ → src/contexts/
@data/ → src/data/
@hooks/ → src/hooks/
@layouts/ → src/layouts/
@lib/ → src/lib/
@mocks/ → src/mocks/
@providers/ → src/providers/
@routes/ → src/routes/
@services/ → src/services/
@store/ → src/store/
@styles/ → src/styles/
@theme/ → src/theme/
@utils/ → src/utils/
@variables/ → src/variables/
@views/ → src/views/
Webpack 优化 (craco.config.js)
性能特性:
- 文件系统缓存(重新构建速度提升 50-80%)
- 按库激进的代码分割:
react-vendor- React 核心(优先级 30)charts-lib- echarts、d3、apexcharts、recharts(优先级 25)chakra-ui- Chakra UI + Emotion(优先级 23)antd-lib- Ant Design(优先级 22)three-lib- Three.js(优先级 20)calendar-lib- moment、date-fns、FullCalendar(优先级 18)
- 从构建中移除 ESLint 插件(速度提升 20-30%)
- 启用 Babel 缓存
- moment locale 剥离(IgnorePlugin)
- Source maps:生产环境禁用,开发环境使用
eval-cheap-module-source-map
开发服务器:
- 端口 3000(prestart 时杀死现有进程)
- 代理(当 MSW 禁用时):
/api→http://49.232.185.254:5001 - Bundle 分析器:
ANALYZE=true npm run build:analyze
构建流程
npm run build使用 CRACO + webpack 优化编译- Gulp 任务(
gulp licenses)为 JS/HTML 添加 Creative Tim 许可证头 - 输出:
build/目录
Node 兼容性:
NODE_OPTIONS='--openssl-legacy-provider --max_old_space_size=4096'
开发工作流
路由开发
添加新路由的步骤:
- 在
src/routes/lazy-components.js中添加 lazy import - 在
src/routes/routeConfig.js中添加路由配置:path- URL 路径component- 来自 lazyComponentsprotection- MODAL/REDIRECT/PUBLIClayout- 'main'(带导航栏)或 'none'(全屏)
- 路由自动使用 Suspense + ErrorBoundary 渲染(由 PageTransitionWrapper 处理)
组件组织模式
基于最近的重构(详见 README.md):
原子设计模式:
- Atoms(原子) - 基础 UI 元素(按钮、徽章、输入框)
- Molecules(分子) - 原子的组合(卡片、表单)
- Organisms(有机体) - 复杂组件(列表、面板)
大型组件示例结构(1000+ 行):
src/views/Community/components/
├── EventCard/
│ ├── index.js - 智能包装器(路由紧凑 vs 详细视图)
│ ├── CompactEventCard.js - 紧凑视图
│ ├── DetailedEventCard.js - 详细视图
│ ├── EventTimeline.js - 原子组件
│ ├── EventImportanceBadge.js - 原子组件
│ └── ...
工具提取:
- 将可复用逻辑提取到
src/utils/(如priceFormatters.js) - 将共享常量提取到
src/constants/(如animations.js)
API 集成
服务层(src/services/):
- 使用
src/utils/apiConfig.js中的getApiBase()获取基础 URL - 示例:
${getApiBase()}/api/endpoint - 在 mock 模式下,MSW 拦截;在开发/生产环境下,请求后端
添加新的 API 端点:
- 在
src/services/中添加服务函数(或在组件中内联) - 如果使用 MSW:在
src/mocks/handlers/{domain}.js中添加 handler - 在
src/mocks/handlers/index.js中导入 handler
Redux 状态管理
现有 slices(src/store/slices/):
authModalSlice- 认证模态框状态posthogSlice- PostHog 分析stockSlice- 股票数据industrySlice- 行业/概念数据subscriptionSlice- 用户订阅communityDataSlice- 社区内容
添加新 slice:
- 创建
src/store/slices/yourSlice.js - 在
src/store/index.js中导入并添加 - 通过
useSelector访问,通过useDispatch派发
TypeScript 接入
概述
项目已于 2025-11-13 完成 TypeScript 环境配置,采用渐进式迁移策略,支持 JavaScript 和 TypeScript 混合开发。
当前状态: 环境配置完成,准备开始代码迁移
迁移策略: 新代码使用 TypeScript,旧代码按优先级逐步迁移
类型严格度: 推荐模式(noImplicitAny: true,其他严格检查待后续开启)
已完成的环境配置
✅ TypeScript 编译器: v5.9.3
✅ tsconfig.json: 推荐模式配置,支持 JS/TS 混合开发
✅ CRACO 配置: 支持 .ts 和 .tsx 文件编译
✅ ESLint 配置: 支持 TypeScript 语法检查
✅ 路径别名: 与现有 @/ 别名保持一致
✅ 全局类型定义: 基础类型文件已创建在 src/types/
可用类型定义
项目已创建以下基础类型定义文件:
src/types/api.ts - API 相关类型
ApiResponse<T>- 通用 API 响应结构PaginatedResponse<T>- 分页响应ApiError- API 错误类型ListQueryParams- 列表查询参数
src/types/stock.ts - 股票相关类型
StockInfo- 股票基础信息StockQuote- 股票行情数据KLineData- K 线数据StockFinancials- 财务指标StockPosition- 股票持仓Sector- 概念/行业板块
src/types/user.ts - 用户相关类型
UserInfo- 用户基础信息AuthInfo- 认证信息LoginParams/RegisterParams- 登录/注册参数UserSubscription- 订阅信息UserAccount- 资金账户UserSettings- 用户设置
使用方式:
// 统一导入
import type { StockQuote, UserInfo, ApiResponse } from '@/types';
// 或从具体文件导入
import type { StockQuote } from '@/types/stock';
TypeScript 命令
# 类型检查(不生成输出文件)
npm run type-check
# 类型检查 + 监听模式
npm run type-check:watch
# ESLint 检查(包含 TS 文件)
npm run lint:check
# ESLint 自动修复
npm run lint:fix
迁移路线图
详细的迁移指南请参考 TYPESCRIPT_MIGRATION.md 文档。
简要路线图:
-
优先级 1️⃣: 工具层(
src/utils/,src/constants/)- 纯函数,迁移成本低,收益高
- 提供类型定义给其他模块使用
-
优先级 2️⃣: 类型定义层(扩展
src/types/)- 添加
trading.ts,community.ts,chart.ts等
- 添加
-
优先级 3️⃣: 服务层(
src/services/)- 定义 API 请求/响应类型
- 使用
ApiResponse<T>包装响应
-
优先级 4️⃣: Redux 状态层(
src/store/slices/)- 定义
RootState和AppDispatch类型 - 创建类型化的 hooks
- 定义
-
优先级 5️⃣: 自定义 Hooks(
src/hooks/)- 添加泛型支持
- 定义完整返回值类型
-
优先级 6️⃣: 组件层(
src/components/,src/views/)- Atoms → Molecules → Organisms → Pages
- 优先迁移复用度高的组件
开发规范
新代码:
- ✅ 必须使用 TypeScript(
.ts或.tsx) - ✅ 所有函数参数和返回值添加类型
- ✅ 组件 Props 使用
interface定义 - ✅ 避免使用
any(特殊情况需添加注释说明)
旧代码迁移:
- 按优先级迁移,不强制一次性完成
- 迁移前先阅读 TYPESCRIPT_MIGRATION.md
- 迁移后运行
npm run type-check验证
类型定义:
- 公共类型定义导出到
src/types/ - 组件内部类型可定义在组件文件中
- 使用
type还是interface参考 迁移指南
常见问题
Q: 路径别名 @/types 无法识别?
A: 确保在 tsconfig.json 中配置了 paths,并重启 IDE。使用 npm run type-check 而非命令行 tsc。
Q: 如何处理第三方库没有类型定义? A:
- 尝试安装
@types/library-name - 创建自定义类型声明文件
src/types/library-name.d.ts - 临时使用
as any(需添加 TODO 注释)
Q: 迁移期间如何处理 any 类型? A: 添加 ESLint 禁用注释和 TODO 说明:
/* eslint-disable @typescript-eslint/no-explicit-any */
// TODO: 待完善类型定义
const legacyFunction = (data: any): any => { ... };
Q: React 组件的 children 类型如何定义?
A: 使用 React.ReactNode:
interface Props {
children: React.ReactNode;
}
参考资源
- TYPESCRIPT_MIGRATION.md - 完整迁移指南
- TypeScript 官方文档
- React TypeScript Cheatsheet
- Redux Toolkit TypeScript 指南
更新本文档
本 CLAUDE.md 文件是一个持续更新的文档。在以下情况下应更新它:
- 添加新的架构模式或指南
- 发现新的最佳实践
- 解决或添加技术债务项
- 做出重要的技术决策
- 进行重要的代码清理或重构
所有开发人员应在入职时审查本文档,并在做出架构决策时参考它。