From dcc88251dfc32bb84622d0997f97bf2fcc7a323b Mon Sep 17 00:00:00 2001 From: zzlgreat Date: Fri, 7 Nov 2025 07:35:13 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E6=83=A0=E7=A0=81Bug=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 340 +++++++++++++----- .../Subscription/SubscriptionContent.js | 27 +- 2 files changed, 281 insertions(+), 86 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 31a10853..0b2a0a43 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,24 +4,24 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -This is a hybrid React dashboard application with a Flask/Python backend for financial/trading analysis. Built on the Argon Dashboard Chakra PRO template with extensive customization. +Hybrid React dashboard for financial/trading analysis with Flask backend. Built on Argon Dashboard Chakra PRO template. -### Frontend (React + Chakra UI) -- **Framework**: React 18.3.1 with Chakra UI 2.8.2 -- **State Management**: Redux Toolkit (@reduxjs/toolkit) -- **Routing**: React Router DOM v6 with lazy loading for code splitting -- **Styling**: Tailwind CSS + custom Chakra theme -- **Build Tool**: CRACO (Create React App Configuration Override) with custom webpack optimizations -- **Charts**: ApexCharts, ECharts, Recharts, D3 -- **UI Components**: Ant Design (antd) alongside Chakra UI -- **Other Libraries**: Three.js (@react-three), FullCalendar, Leaflet maps +### Tech Stack -### Backend (Flask/Python) -- **Framework**: Flask with SQLAlchemy ORM -- **Database**: ClickHouse for analytics queries + MySQL/PostgreSQL -- **Real-time**: Flask-SocketIO for WebSocket connections -- **Task Queue**: Celery with Redis for background processing -- **External APIs**: Tencent Cloud SMS, WeChat Pay integration +**Frontend** +- React 18.3.1 + Chakra UI 2.8.2 + Ant Design +- Redux Toolkit for state management +- React Router v6 with React.lazy() code splitting +- CRACO build system with aggressive webpack optimization +- Charts: ApexCharts, ECharts, Recharts, D3 +- Additional: Three.js, FullCalendar, Leaflet maps + +**Backend** +- Flask + SQLAlchemy ORM +- ClickHouse (analytics) + MySQL/PostgreSQL (transactional) +- Flask-SocketIO for WebSocket real-time updates +- Celery + Redis for background jobs +- Tencent Cloud SMS + WeChat Pay integration ## Development Commands @@ -46,89 +46,265 @@ npm run reinstall # Clean install (runs clean + install) ### Backend Development ```bash -python app.py # Main Flask server (newer version) -python app_2.py # Flask server (appears to be current main) -python simulation_background_processor.py # Background data processor for simulations +python app.py # Main Flask server +python simulation_background_processor.py # Background task processor for trading simulations +pip install -r requirements.txt # Install Python dependencies ``` ### Deployment ```bash -npm run deploy # Executes scripts/deploy-from-local.sh -npm run deploy:setup # Setup deployment (scripts/setup-deployment.sh) -npm run rollback # Rollback deployment (scripts/rollback-from-local.sh) -``` - -### Python Dependencies -```bash -pip install -r requirements.txt +npm run deploy # Deploy from local (scripts/deploy-from-local.sh) +npm run rollback # Rollback to previous version ``` ## Architecture -### Frontend Structure -- **src/App.js** - Main application entry with route definitions (routing moved from src/routes.js) -- **src/layouts/** - Layout wrappers (Auth, Home, MainLayout) -- **src/views/** - Page components (Community, Company, TradingSimulation, etc.) -- **src/components/** - Reusable UI components -- **src/contexts/** - React contexts (AuthContext, NotificationContext, IndustryContext) -- **src/store/** - Redux store with slices (posthogSlice, etc.) -- **src/services/** - API service layer -- **src/theme/** - Chakra UI theme customization -- **src/mocks/** - MSW (Mock Service Worker) handlers for development - - src/mocks/handlers/ - Request handlers by domain - - src/mocks/data/ - Mock data files - - src/mocks/browser.js - MSW browser setup +### Application Entry Flow +``` +src/index.js +└── src/App.js (root component) + ├── AppProviders (src/providers/AppProviders.js) + │ ├── ReduxProvider (store from src/store/) + │ ├── ChakraProvider (theme from src/theme/) + │ ├── NotificationProvider (src/contexts/NotificationContext.js) + │ └── AuthProvider (src/contexts/AuthContext.js) + ├── AppRoutes (src/routes/index.js) + │ ├── MainLayout routes (with navbar/footer) + │ └── Standalone routes (auth pages, fullscreen views) + └── GlobalComponents (modal overlays, global UI) +``` + +### Routing Architecture (Modular Design) +Routing is **declarative** and split across multiple files in `src/routes/`: + +- **index.js** - Main router (combines config + renders routes) +- **routeConfig.js** - Route definitions (path, component, protection, layout, children) +- **lazy-components.js** - React.lazy() imports for code splitting +- **homeRoutes.js** - Nested home page routes +- **constants/** - Protection modes, layout mappings +- **utils/** - Route rendering logic (wrapWithProtection, renderRoute) + +Route protection modes (PROTECTION_MODES): +- `PUBLIC` - No authentication required +- `MODAL` - Shows auth modal if not logged in +- `REDIRECT` - Redirects to /auth/sign-in if not logged in + +### Frontend Directory Structure +``` +src/ +├── App.js - Root component (providers + routing) +├── providers/ - Provider composition (AppProviders.js) +├── routes/ - Modular routing system (see above) +├── layouts/ - Page layouts (MainLayout, Auth) +├── views/ - Page components (Community, TradingSimulation, etc.) +├── components/ - Reusable UI components +├── contexts/ - React contexts (Auth, Notification, Sidebar) +├── store/ - Redux store + slices (auth, posthog, stock, industry, etc.) +├── services/ - API layer (axios wrappers) +├── utils/ - Utility functions (apiConfig, priceFormatters, logger) +├── constants/ - App constants (animations, etc.) +├── hooks/ - Custom React hooks +├── theme/ - Chakra UI theme customization +└── mocks/ - MSW handlers for development + ├── handlers/ - Request handlers by domain (auth, stock, company, etc.) + ├── data/ - Mock data files + └── browser.js - MSW setup (starts when REACT_APP_ENABLE_MOCK=true) +``` ### Backend Structure -- **app.py / app_2.py** - Main Flask application with routes, authentication, and business logic -- **simulation_background_processor.py** - Background processor for trading simulations -- **wechat_pay.py / wechat_pay_config.py** - WeChat payment integration -- **concept_api.py** - API for concept/industry analysis -- **tdays.csv** - Trading days calendar data (loaded into memory at startup) +``` +app.py - Flask server (routes, auth, business logic) +simulation_background_processor.py - Celery worker for trading simulations +concept_api.py - Concept/industry analysis API +wechat_pay.py / wechat_pay_config.py - WeChat payment integration +tdays.csv - Trading days calendar (loaded at startup) +requirements.txt - Python dependencies +``` -### Key Integrations -- **ClickHouse** - High-performance analytics queries -- **Celery + Redis** - Background task processing -- **Flask-SocketIO** - Real-time data updates via WebSocket -- **Tencent Cloud** - SMS services -- **WeChat Pay** - Payment processing -- **PostHog** - Analytics (initialized in Redux) -- **MSW** - API mocking for development/testing +### State Management Strategy +- **Redux Toolkit**: Global state (auth modal, posthog, stock data, industry data, subscriptions, community data) +- **React Context**: Cross-cutting concerns (AuthContext, NotificationContext, SidebarContext) +- **Component State**: Local UI state (forms, toggles, etc.) -### Routing & Code Splitting -- Routing is defined in **src/App.js** (not src/routes.js - that file is deprecated) -- Heavy components use React.lazy() for code splitting (Community, TradingSimulation, etc.) -- Protected routes use ProtectedRoute and ProtectedRouteRedirect components +### Real-time Updates +- Flask-SocketIO for WebSocket connections +- Example: Community event notifications push via WebSocket +- Client: `socket.io-client` library ## Configuration ### Environment Files -Multiple environment configurations available: -- **.env.mock** - Mock data mode (default for `npm start`) -- **.env.local** - Real backend connection -- **.env.development** - Development environment -- **.env.test** - Test environment +``` +.env.mock - Mock mode (default): MSW intercepts all API calls, no backend needed +.env.development - Dev mode: Connects to dev backend +.env.test - Test mode: Used by 'npm run start:test' (backend + frontend together) +.env.production - Production build config +``` -### Build Configuration (craco.config.js) -- **Webpack caching**: Filesystem cache for faster rebuilds (50-80% improvement) -- **Code splitting**: Aggressive chunk splitting by library (react-vendor, charts-lib, chakra-ui, antd-lib, three-lib, etc.) -- **Path aliases**: `@` → src/, `@components` → src/components/, `@views` → src/views/, `@assets` → src/assets/, `@contexts` → src/contexts/ -- **Optimizations**: ESLint plugin removed from build for speed, Babel caching enabled, moment locale stripping -- **Source maps**: Disabled in production, eval-cheap-module-source-map in development -- **Dev server proxy**: `/api` requests proxy to http://49.232.185.254:5001 +**Key environment variables:** +- `REACT_APP_ENABLE_MOCK=true` - Enable MSW mocking +- `REACT_APP_API_URL` - Backend URL (empty string = use relative paths or MSW) -### Important Build Notes -- Uses NODE_OPTIONS='--openssl-legacy-provider --max_old_space_size=4096' for Node compatibility -- Gulp task adds Creative Tim license headers post-build -- Bundle analyzer available via `ANALYZE=true npm run build:analyze` -- Pre-build: kills any process on port 3000 +### MSW (Mock Service Worker) Setup +MSW is used for API mocking during development: -## Testing -- **React Testing Library** for component tests -- **MSW** (Mock Service Worker) for API mocking during tests -- Run tests: `npm test` +1. **Activation**: Set `REACT_APP_ENABLE_MOCK=true` in env file +2. **Worker file**: `public/mockServiceWorker.js` (auto-generated) +3. **Handlers**: `src/mocks/handlers/` (organized by domain: auth, stock, company, etc.) +4. **Mode**: `onUnhandledRequest: 'warn'` - unhandled requests pass through to backend -## Deployment -- Deployment scripts in **scripts/** directory -- Build output processed by Gulp for licensing -- Supports rollback via scripts/rollback-from-local.sh \ No newline at end of file +When MSW is active, the dev server proxy is disabled (MSW intercepts first). + +### Path Aliases (craco.config.js) +All aliases resolve to `src/` subdirectories: +``` +@/ → src/ +@components/ → src/components/ +@views/ → src/views/ +@assets/ → src/assets/ +@contexts/ → src/contexts/ +@layouts/ → src/layouts/ +@services/ → src/services/ +@store/ → src/store/ +@utils/ → src/utils/ +@hooks/ → src/hooks/ +@theme/ → src/theme/ +@mocks/ → src/mocks/ +@constants/ → src/constants/ +``` + +### Webpack Optimizations (craco.config.js) +**Performance features:** +- Filesystem cache (50-80% rebuild speedup) +- Aggressive code splitting by library: + - `react-vendor` - React core (priority 30) + - `charts-lib` - echarts, d3, apexcharts, recharts (priority 25) + - `chakra-ui` - Chakra UI + Emotion (priority 23) + - `antd-lib` - Ant Design (priority 22) + - `three-lib` - Three.js (priority 20) + - `calendar-lib` - moment, date-fns, FullCalendar (priority 18) +- ESLint plugin removed from build (20-30% speedup) +- Babel caching enabled +- moment locale stripping (IgnorePlugin) +- Source maps: disabled in production, `eval-cheap-module-source-map` in dev + +**Dev server:** +- Port 3000 (kills existing process on prestart) +- Proxy (when MSW disabled): `/api` → `http://49.232.185.254:5001` +- Bundle analyzer: `ANALYZE=true npm run build:analyze` + +### Build Process +1. `npm run build` compiles with CRACO + webpack optimizations +2. Gulp task (`gulp licenses`) adds Creative Tim license headers to JS/HTML +3. Output: `build/` directory + +**Node compatibility:** +```bash +NODE_OPTIONS='--openssl-legacy-provider --max_old_space_size=4096' +``` + +## Development Workflow + +### Working with Routes +To add a new route: + +1. Add lazy import in `src/routes/lazy-components.js` +2. Add route config in `src/routes/routeConfig.js` with: + - `path` - URL path + - `component` - From lazyComponents + - `protection` - MODAL/REDIRECT/PUBLIC + - `layout` - 'main' (with nav) or 'none' (fullscreen) +3. Routes automatically render with Suspense + ErrorBoundary (handled by PageTransitionWrapper) + +### Component Organization Patterns +Based on recent refactoring (see README.md for details): + +**Atomic Design Pattern:** +- **Atoms** - Basic UI elements (buttons, badges, inputs) +- **Molecules** - Combinations of atoms (cards, forms) +- **Organisms** - Complex components (lists, panels) + +**Example structure for large components (1000+ lines):** +``` +src/views/Community/components/ +├── EventCard/ +│ ├── index.js - Smart wrapper (routes compact vs detailed) +│ ├── CompactEventCard.js - Compact view +│ ├── DetailedEventCard.js - Detailed view +│ ├── EventTimeline.js - Atomic component +│ ├── EventImportanceBadge.js - Atomic component +│ └── ... +``` + +**Utility extraction:** +- Extract reusable logic to `src/utils/` (e.g., `priceFormatters.js`) +- Extract shared constants to `src/constants/` (e.g., `animations.js`) + +### API Integration +**Service layer** (`src/services/`): +- Use `getApiBase()` from `src/utils/apiConfig.js` for base URL +- Example: `${getApiBase()}/api/endpoint` +- In mock mode, MSW intercepts; in dev/prod, hits backend + +**Adding new API endpoints:** +1. Add service function in `src/services/` (or inline in component) +2. If using MSW: Add handler in `src/mocks/handlers/{domain}.js` +3. Import handler in `src/mocks/handlers/index.js` + +### Redux State Management +**Existing slices** (`src/store/slices/`): +- `authModalSlice` - Auth modal state +- `posthogSlice` - PostHog analytics +- `stockSlice` - Stock data +- `industrySlice` - Industry/concept data +- `subscriptionSlice` - User subscriptions +- `communityDataSlice` - Community content + +**Adding new slice:** +1. Create `src/store/slices/yourSlice.js` +2. Import and add to `src/store/index.js` +3. Access via `useSelector`, dispatch via `useDispatch` + +## Backend Architecture + +### Flask Application (app.py) +- **Authentication**: Flask-Login + session management +- **Database**: SQLAlchemy models + ClickHouse client +- **Background jobs**: Celery tasks for async processing +- **Real-time**: Flask-SocketIO for WebSocket events +- **Trading days**: `tdays.csv` loaded into memory at startup (global `trading_days` variable) + +### Key Backend Patterns +- **ClickHouse**: Used for high-volume analytics queries (stock data, time series) +- **MySQL/PostgreSQL**: Used for transactional data (users, orders, subscriptions) +- **Celery**: Background processor runs in separate process (`simulation_background_processor.py`) +- **CORS**: Enabled for frontend communication + +### API Proxy Configuration +When not in mock mode, frontend proxies to backend: +- `/api` → `http://49.232.185.254:5001` (main API) +- `/concept-api` → `http://49.232.185.254:6801` (concept analysis API) + +## Important Notes + +### Code Splitting Strategy +Heavy pages are lazy-loaded to reduce initial bundle size: +- Community, TradingSimulation, Company pages use `React.lazy()` +- Webpack splits large libraries into separate chunks +- Check bundle size with `npm run build:analyze` + +### Error Boundaries +- **Layout-level**: Each layout (MainLayout, Auth) has its own ErrorBoundary +- **Page-level**: PageTransitionWrapper wraps each route with ErrorBoundary +- **Strategy**: Errors are isolated to prevent entire app crashes + +### PostHog Analytics +- Initialized in Redux (`posthogSlice`) +- Configured during app startup in `App.js` +- Used for user behavior tracking + +### Performance Considerations +- **Large components**: If component exceeds ~500 lines, consider refactoring (see README.md) +- **Re-renders**: Use `React.memo`, `useMemo`, `useCallback` for expensive operations +- **Bundle size**: Monitor with webpack-bundle-analyzer +- **Caching**: Webpack filesystem cache speeds up rebuilds significantly \ No newline at end of file diff --git a/src/components/Subscription/SubscriptionContent.js b/src/components/Subscription/SubscriptionContent.js index c621516e..f66d60a7 100644 --- a/src/components/Subscription/SubscriptionContent.js +++ b/src/components/Subscription/SubscriptionContent.js @@ -162,6 +162,18 @@ export default function SubscriptionContent() { // 计算价格(包含升级和优惠码) const calculatePrice = async (plan, cycle, promoCodeValue = null) => { try { + // 确保优惠码值正确:只接受非空字符串,其他情况传null + const validPromoCode = promoCodeValue && typeof promoCodeValue === 'string' && promoCodeValue.trim() + ? promoCodeValue.trim() + : null; + + logger.debug('SubscriptionContent', '计算价格', { + plan: plan.name, + cycle, + promoCodeValue, + validPromoCode + }); + const response = await fetch('/api/subscription/calculate-price', { method: 'POST', headers: { @@ -171,7 +183,7 @@ export default function SubscriptionContent() { body: JSON.stringify({ to_plan: plan.name, to_cycle: cycle, - promo_code: promoCodeValue || null + promo_code: validPromoCode }) }); @@ -191,7 +203,9 @@ export default function SubscriptionContent() { // 验证优惠码 const handleValidatePromoCode = async () => { - if (!promoCode.trim()) { + const trimmedCode = promoCode.trim(); + + if (!trimmedCode) { setPromoCodeError('请输入优惠码'); return; } @@ -205,8 +219,8 @@ export default function SubscriptionContent() { setPromoCodeError(''); try { - // 重新计算价格,包含优惠码 - const result = await calculatePrice(selectedPlan, selectedCycle, promoCode); + // 重新计算价格,包含优惠码(使用去除空格后的值) + const result = await calculatePrice(selectedPlan, selectedCycle, trimmedCode); if (result && !result.promo_error) { setPromoCodeApplied(true); @@ -1171,6 +1185,11 @@ export default function SubscriptionContent() { stopAutoPaymentCheck(); setPaymentOrder(null); setPaymentCountdown(0); + // 清空优惠码状态 + setPromoCode(''); + setPromoCodeApplied(false); + setPromoCodeError(''); + setPriceInfo(null); onPaymentModalClose(); }} size="lg"