# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview This is a **hybrid ERP system** consisting of: 1. **Backend**: RuoYi-Vue (Spring Boot 2.5.15 + MyBatis) - Java 17 management system 2. **Desktop Client**: Electron + Vue 3 + TypeScript application 3. **Embedded Spring Boot Service**: Java service that runs within the Electron app The architecture uses a dual-service pattern where the Electron app communicates with both: - Local embedded Spring Boot service (port 8081) - Remote RuoYi admin backend (port 8085) ## Repository Structure ``` C:\wox\erp\ ├── electron-vue-template/ # Electron + Vue 3 desktop client │ ├── src/ │ │ ├── main/ # Electron main process (TypeScript) │ │ └── renderer/ # Vue 3 renderer process │ │ ├── api/ # API client modules │ │ ├── components/ # Vue components │ │ └── utils/ # Utility functions │ ├── scripts/ # Build scripts │ └── package.json ├── ruoyi-admin/ # Main Spring Boot application entry ├── ruoyi-system/ # System management module ├── ruoyi-framework/ # Framework core (Security, Redis, etc.) ├── ruoyi-common/ # Common utilities ├── ruoyi-generator/ # Code generator ├── ruoyi-quartz/ # Scheduled tasks ├── erp_client_sb/ # Embedded Spring Boot service for client ├── sql/ # Database migration scripts └── pom.xml # Root Maven configuration ``` ## Development Commands ### Backend (Spring Boot) ```bash # Build the project (from root) mvn clean package # Run the RuoYi admin backend cd ruoyi-admin mvn spring-boot:run # Runs on http://localhost:8085 # Build without tests mvn clean package -DskipTests ``` ### Frontend (Electron + Vue) ```bash cd electron-vue-template # Install dependencies npm install # Development mode with hot reload npm run dev # Build for distribution npm run build # Cross-platform npm run build:win # Windows npm run build:mac # macOS npm run build:linux # Linux ``` ## Key Architecture Patterns ### 1. Dual-Backend Routing (http.ts) The Electron client uses intelligent routing to determine which backend to call: - Paths starting with `/monitor/`, `/system/`, `/tool/banma`, `/tool/genmai` → RuoYi backend (port 8085) - All other paths → Embedded Spring Boot service (port 8081) **Location**: `electron-vue-template/src/renderer/api/http.ts` ### 2. Account-Based Resource Isolation User-specific resources (splash images, brand logos) are stored per account: - Backend stores URLs in the `client_account` table (columns: `splash_image`, `brand_logo`) - Files are uploaded to Qiniu Cloud (七牛云) configured in `application.yml` - Each user sees only their own uploaded assets **Key files**: - Java entity: `ruoyi-system/src/main/java/com/ruoyi/system/domain/ClientAccount.java` - MyBatis mapper: `ruoyi-system/src/main/resources/mapper/system/ClientAccountMapper.xml` - API endpoints: `ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ClientAccountController.java` ### 3. Event-Driven UI Updates The Vue application uses custom browser events to propagate state changes between components: - `window.dispatchEvent(new CustomEvent('brandLogoChanged'))` - notifies when brand logo changes - `window.addEventListener('brandLogoChanged', handler)` - listens for changes in App.vue This pattern ensures immediate UI updates after upload/delete operations without requiring page refreshes. ### 4. VIP Feature Gating Certain features (e.g., custom splash images, brand logos) are gated by VIP status: - Check `accountType` field in `ClientAccount` (values: `trial`, `paid`) - Trial accounts show `TrialExpiredDialog` when attempting VIP features - VIP validation happens in `SettingsDialog.vue` before allowing uploads ## Important Configuration ### Backend Configuration **File**: `ruoyi-admin/src/main/resources/application.yml` Key settings: - Server port: `8085` - Upload path: `ruoyi.profile: D:/ruoyi/uploadPath` - Redis: `8.138.23.49:6379` (password: `123123`) - Qiniu Cloud credentials for file storage - Token expiration: 30 minutes ### Database The system uses MySQL with MyBatis. When adding new fields: 1. Write SQL migration script in `sql/` directory 2. Update Java entity in `ruoyi-system/src/main/java/com/ruoyi/system/domain/` 3. Update MyBatis mapper XML in `ruoyi-system/src/main/resources/mapper/system/` 4. Include field in ``, ``, ``, and `` sections ### Electron Main Process **File**: `electron-vue-template/src/main/main.ts` - Manages embedded Spring Boot process lifecycle - Handles splash screen display - Configures tray icon - Manages auto-updates - Uses app data directory: `app.getPath('userData')` ## Development Workflow (from .cursor/rules/guize.mdc) When making code changes, follow this three-phase approach: ### Phase 1: Analyze Problem (【分析问题】) - Understand user intent and ask clarifying questions - Search all related code - Identify root cause - Look for code smells: duplication, poor naming, outdated patterns, inconsistent types - Ask questions if multiple solutions exist ### Phase 2: Plan Solution (【制定方案】) - List files to be created/modified/deleted - Describe changes briefly for each file - Eliminate code duplication through reuse/abstraction - Ensure DRY principles and good architecture - Ask questions if key decisions are unclear ### Phase 3: Execute (【执行方案】) - Implement according to the approved plan - Run type checking after modifications - **DO NOT** commit code unless explicitly requested - **DO NOT** start dev servers automatically ## Common Patterns ### Adding a New API Endpoint 1. **Backend** (Spring Boot): ```java // In appropriate Controller (e.g., ClientAccountController.java) @PostMapping("/your-endpoint") public AjaxResult yourMethod(@RequestBody YourDTO dto) { // Implementation return AjaxResult.success(result); } ``` 2. **Frontend** (Vue/TypeScript): ```typescript // In electron-vue-template/src/renderer/api/your-module.ts export const yourApi = { async yourMethod(data: YourType) { return http.post('/your-endpoint', data) } } ``` 3. **Component usage**: ```vue ``` ### File Upload Pattern ```typescript // Frontend const handleUpload = async (file: File) => { const formData = new FormData() formData.append('file', file) formData.append('username', currentUsername) const res = await splashApi.uploadSomething(file, username) if (res.url) { localImageUrl.value = res.url // Update immediately window.dispatchEvent(new CustomEvent('imageChanged')) } } ``` ```java // Backend Controller @PostMapping("/upload") public AjaxResult upload(@RequestParam("file") MultipartFile file) { String url = qiniuService.uploadFile(file); // Save URL to database return AjaxResult.success(url); } ``` ## Technology Stack Details ### Backend - **Framework**: Spring Boot 2.5.15 - **Security**: Spring Security 5.7.12 + JWT - **ORM**: MyBatis with PageHelper - **Database**: MySQL - **Cache**: Redis (Lettuce client) - **File Storage**: Qiniu Cloud (七牛云) - **API Docs**: Swagger 3.0.0 - **Build**: Maven ### Frontend - **Framework**: Vue 3.3.8 (Composition API with `