# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview This is a multi-tenant service management platform ("百业到家") built with Ant Design Pro, React 19, TypeScript, and UmiJS Max framework. The application uses dynamic routing loaded from a backend API and implements a comprehensive permission system. ## Development Commands ```bash # Start development server (with mocks) pnpm dev # or pnpm start # Start development server (without mocks, connects to backend) pnpm start:dev # Build for production pnpm build # Lint (Biome + TypeScript check) pnpm lint # Run tests pnpm test # Run tests with coverage pnpm test:coverage # Type check only pnpm tsc ``` **Important**: This project uses **pnpm** as the package manager, not npm or yarn. ## Architecture ### Dynamic Routing System Routes are **not** statically defined in `config/routes.ts` (most routes are commented out). Instead, routes are dynamically loaded from the backend API: 1. On app initialization, `getInfo()` fetches user permissions and menu structure 2. `patchClientRoutes()` in `src/app.tsx` transforms backend menu data into UmiJS routes 3. `loopMenuItem()` in `src/utils/menuUtils.tsx` recursively converts menu items to route objects 4. Routes are lazy-loaded using `React.lazy(() => import('@/pages/${component}'))` **When adding new pages**: Create the page component in `src/pages/`, then configure the route in the backend system (not in code). ### Multi-Tenant Architecture Every API request includes a `tenant-id` header. Tenant information is: - Stored in web-storage-cache with key `CACHE_KEY.TenantId` - Set during login via `setTenantId()` - Automatically added to all requests by the request interceptor in `src/app.tsx` ### Authentication Flow 1. User logs in with tenant name, username, and password (password is RSA encrypted via JSEncrypt) 2. Backend returns `accessToken` and `refreshToken` 3. Tokens stored in web-storage-cache (see `src/utils/auth.ts`) 4. All requests include `Authorization: Bearer ${accessToken}` header 5. On 401 response, automatically attempts token refresh using `refreshToken` 6. If refresh fails, redirects to login page **Token refresh logic** is in `src/requestErrorConfig.ts` with request queuing to prevent duplicate refresh attempts. ### Request Interceptors All API requests are automatically modified (see `src/app.tsx`): - **API prefix**: `/admin-api` is prepended to all URLs (unless already present) - **Headers**: `Authorization`, `tenant-id`, and `Accept` headers are added - **Parameter serialization**: Arrays are serialized specially: - `createTime` arrays become `createTime[0]` and `createTime[1]` - Other arrays become `key[]` format ### Caching Strategy Uses `web-storage-cache` library (see `src/hooks/web/useCache.ts`): - `CACHE_KEY.USER`: Current user info - `CACHE_KEY.ROLE_ROUTERS`: Dynamic menu/route data - `CACHE_KEY.ACCESS_TOKEN` / `CACHE_KEY.REFRESH_TOKEN`: Auth tokens - `CACHE_KEY.DICT_CACHE`: Dictionary/enum data loaded on app init - `CACHE_KEY.LoginForm`: Remember me functionality (password encrypted) ### State Management - **UmiJS Model Plugin**: For global state (see `src/app.tsx` - `initialState`) - **Custom Hooks**: In `src/hooks/stores/` (e.g., `useDictStore` for dictionary management) - **Web Storage Cache**: For persistent data across sessions ## Project Structure ``` src/ ├── pages/ # Feature pages organized by domain │ ├── ai/ # AI model and sample management │ ├── prod/ # Product and category management │ ├── system/ # System admin (users, roles, menus, tenants, logs, messages) │ ├── trade/ # Order and sales management │ └── user/ # Login page ├── services/ # API service layer (mirrors page structure) ├── components/ # Reusable components │ ├── Draggable/ # Drag-and-drop components using @dnd-kit │ ├── Upload/ # File/image/video upload components │ ├── GroupTag/ # Tag management components │ └── Tinymce/ # Rich text editor wrapper ├── hooks/ # Custom React hooks │ ├── antd/ # Ant Design related hooks │ ├── stores/ # State management hooks │ └── web/ # Web utilities (cache, etc.) ├── utils/ # Utility functions │ ├── auth.ts # Token and tenant management │ ├── menuUtils.tsx # Dynamic route conversion │ ├── dict.ts # Dictionary/enum utilities │ └── tree.ts # Tree data structure helpers ├── constants/ # Shared constants and enums ├── locales/ # i18n translations (zh-CN default) └── app.tsx # App initialization and runtime config ``` ## Environment Configuration Multiple environment files: - `.env.local` - Local overrides (gitignored) - `.env.dev` - Development environment - `.env.prod` - Production environment **Proxy configuration** in `config/proxy.ts`: - `dev`: Points to local backend (currently `http://192.168.1.89:48080`) - `test`: Points to test environment - `pre`: Points to pre-production environment Switch environments using `UMI_ENV`: ```bash cross-env UMI_ENV=dev pnpm start # Development cross-env UMI_ENV=test pnpm start # Test cross-env UMI_ENV=pre pnpm start # Pre-production ``` ## Code Conventions ### Linting - Uses **Biome** (not ESLint) for linting and formatting - Configured in `biome.json` - Pre-commit hook runs Biome via lint-staged ### Commit Messages - Uses commitlint with conventional commits - Enforced via Husky pre-commit hook - Format: `type(scope): subject` (e.g., `feat: add user management`) ### Component Patterns - **ProTable**: Use `EnhancedProTable` wrapper for common table functionality - **Forms**: Use `DrawerForm` or `ModalForm` from `@ant-design/pro-components` - **File Uploads**: Use custom `UploadImages` or `UploadVideo` components in `src/components/Upload/` - **Rich Text**: Use `Tinymce` component wrapper (TinyMCE is in `public/tinymce/`) ### Service Layer - All API calls go through `src/services/` - Use UmiJS `request` from `@umijs/max` (configured in `src/app.tsx`) - Response format: `{ code: number, data: any, msg: string }` - Success when `code === 0` ## Common Patterns ### Adding a New Page 1. Create page component in `src/pages/{domain}/{feature}/index.tsx` 2. Create corresponding service in `src/services/{domain}/{feature}/index.ts` 3. Configure the menu/route in the backend system (not in code) 4. The route will automatically appear after backend configuration ### Working with Dictionaries/Enums Dictionaries are loaded on app init and cached: ```typescript import { useDictStore } from '@/hooks/stores/dict'; const dictStore = useDictStore(); const dictData = dictStore.getDictByType('dict_type_key'); ``` ### File Uploads Media files are uploaded via `src/services/infra/file/index.ts` or `src/services/infra/media/index.ts`. Use the Upload components which handle the API integration. ## Testing - Jest configured with `jest.config.ts` - Test files should be colocated with components or in `__tests__` directories - Run single test: `pnpm test -- path/to/test.spec.ts` ## Important Notes - **Node version**: Requires Node.js >= 20.0.0 (see `package.json` engines) - **React 19**: Uses React 19 with `@ant-design/v5-patch-for-react-19` compatibility patch - **Mako bundler**: Uses UmiJS Mako for faster builds (configured in `config/config.ts`) - **Mock data**: Located in `mock/` directory, enabled by default in dev mode - **TinyMCE**: Static files in `public/tinymce/`, Chinese language pack included