# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview This is **RuoYi v3.9.0** (若依), a Chinese Spring Boot + Vue admin framework customized for IoT/Energy Storage Management (储能运营平台). It extends the standard RuoYi RBAC system with IoT device management, time-series data ingestion, MQTT messaging, and vehicle/equipment tracking. - **Backend**: Spring Boot 2.5.15, Java 8, Spring Security 5.7.12, JWT authentication - **Frontend**: Vue 2 + Element UI (separate repository, not in this workspace) - **Documentation**: http://doc.ruoyi.vip ## Build & Run Commands This is a Maven multi-module project. All commands should be run from the repository root. ```bash # Compile all modules mvn clean compile # Package into executable JAR (skips tests — there are no tests in this project) mvn clean package -Dmaven.test.skip=true # Run the application (after packaging) cd ruoyi-admin/target java -jar ruoyi-admin.jar # Or use the provided script ./ry.sh start # start / stop / restart / status ``` The application starts on **port 8887**. The executable JAR is `ruoyi-admin/target/ruoyi-admin.jar`. ## Module Architecture ``` ruoyi-admin → Web layer: REST controllers, Swagger config, application.yml entrypoint ruoyi-framework → Infrastructure: Spring Security config, JWT filter, Druid datasource, Redis config, MyBatis config, AOP aspects (logging, rate limit, data scope) ruoyi-system → Business layer: domain models, mappers (MyBatis), services, service implementations — this is where custom IoT logic lives ruoyi-common → Shared utilities: constants, enums, exception types, utils (StringUtils, SecurityUtils, DateUtils, ServletUtils, etc.) ruoyi-quartz → Scheduled job engine (standard RuoYi) ruoyi-generator → Code generation templates (standard RuoYi) ``` Dependency flow: `admin → framework → system → common`. `quartz` and `generator` are optional add-ons. ## Data Architecture ### Dual MySQL Databases (Druid connection pool) - **Master** (`data`): RuoYi system tables (users, roles, menus, logs, custom business tables) - **Slave** (`cnc`): Secondary data — configured in `application-druid.yml` - Both hosted on `47.104.204.180:3306` - Dynamic datasource switching via `@DataSource` annotation ### TDengine (Time-Series Database) - Used for high-frequency IoT telemetry ingestion - Connection pool: HikariCP via `TDengineService` (`ruoyi-system/.../service/TDengineService.java`) - Connects to `jdbc:TAOS://localhost:6030/` - Super-table pattern: `CREATE STABLE ... (ts TIMESTAMP, surfacename VARCHAR(64)) TAGS (location BINARY(64))` - Column caching in memory (`stableColumnCache`) to avoid repeated `DESCRIBE` calls ### Redis - Host: `localhost:6379` - Key patterns used by custom code: - `DSB:active:devices` — Set of active IoT device Redis keys - `DSB::` — Hash storing device telemetry fields - `workorder:coordinate:` — Hash storing vehicle GPS (latitude, longitude) - `:` / `_cmd:` / `_fault:` — MQTT topic metadata ## Custom Business Domains (Beyond Standard RuoYi) Standard RuoYi provides users, roles, menus, depts, posts, dicts, config, notices, operlog, logininfor. This project adds: | Domain | Purpose | Key Files | |--------|---------|-----------| | **SysDevice** | IoT device telemetry key-value storage | `domain/SysDevice.java`, `mapper/SysDeviceMapper.java`, `service/SysDeviceService.java` | | **SysDeviceControl** | Device configuration / metric whitelist | `domain/SysDeviceControl.java`, `service/SysDeviceControlService.java` | | **SysDeviceVo** | Aggregated device view for frontend | `domain/vo/SysDeviceVo.java`, `service/SysDeviceVoService.java` | | **SysCar** | Vehicle/equipment with GPS tracking | `domain/SysCar.java`, `service/SysCarService.java` | | **SysAlarm** | Alarm events from devices | `domain/SysAlarm.java`, `service/SysAlarmService.java` | | **SysFault** | Fault records | `domain/SysFault.java`, `service/SysFaultService.java` | | **SysWorkorder** | Work orders / service tickets | `domain/SysWorkorder.java`, `service/SysWorkorderService.java` | | **SysCompany** | Partner company management | `domain/SysCompany.java`, `service/SysCompanyService.java` | | **SysIndicators** | Daily business KPIs (order count, profit) | `domain/SysIndicators.java`, `service/SysIndicatorsService.java` | | **SysController** | MQTT topic registry per controller | `domain/SysController.java`, `service/SysControllerService.java` | | **Sysrealtime** | Real-time device data sync from Redis → MySQL | `domain/Sysrealtime.java`, `service/SysrealtimeService.java` | | **ControllerData / topics** | DTOs for MQTT JSON payload parsing | `domain/ControllerData.java`, `domain/topics.java` | | **MqttClientWrapper** | Wrapper around Eclipse Paho MQTT client | `domain/MqttClientWrapper.java` | ## IoT Data Flow ``` MQTT Broker (47.104.204.180:1883) ↓ subscribes to "+/generics" TdengineController2 / TdegnineController3 (MQTT listeners) ↓ parse JSON → ControllerData → topics ├─→ Redis (DSB:active:devices, DSB:: hashes) ├─→ MySQL (sys_controller table — topic registry) └─→ TDengine (time-series telemetry) SysCarController (scheduled tasks, every 30s) ↓ reads Redis DSB:* keys ├─→ syncs to sysrealtime (MySQL) ├─→ updates vehicle GPS in sys_car (MySQL) ├─→ updates sys_indicators KPIs (MySQL) └─→ triggers external webhook (https://esos-iot.com:9443/syscar/trigger) ``` There are **4 `@Scheduled(fixedRate = 30000)` tasks** in `SysCarController`: 1. `updatesyscar()` — sync vehicle GPS from Redis to MySQL, trigger webhook on change 2. `insertdevice()` — sync device config from Redis to `sys_device_vo` 3. `syncRedisToMySQL()` — sync telemetry from Redis to `sysrealtime` 4. `insertindicators()` — aggregate daily KPIs per company into `sys_indicators` ## Key Configuration Files - `ruoyi-admin/src/main/resources/application.yml` — Main config (port 8887, Redis, MyBatis, PageHelper, Swagger, token settings) - `ruoyi-admin/src/main/resources/application-druid.yml` — Database connection (master/slave MySQL, Druid pool) - `ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml` — MyBatis global settings - `ruoyi-admin/src/main/resources/logback.xml` — Logging config ## Authentication & Security - JWT-based stateless auth (token header: `Authorization`) - Spring Security config in `ruoyi-framework/.../config/SecurityConfig.java` - JWT filter: `JwtAuthenticationTokenFilter.java` - Password max retry: 5 attempts, lock time: 10 minutes - Swagger enabled at `/dev-api` path mapping - XSS filtering enabled for `/system/*`, `/monitor/*`, `/tool/*` ## API Response Format All controllers return `AjaxResult` (from `ruoyi-common`), which wraps: ```java { "code": 200, // HttpStatus constant "msg": "操作成功", // message "data": { ... } // payload } ``` Page queries use `TableDataInfo` which wraps: ```java { "code": 200, "msg": "查询成功", "rows": [ ... ], // list data "total": 100 // total count } ``` ## Important Implementation Notes - **No unit tests exist** in this project. Test coverage is 0%. - **Field injection is used everywhere** (`@Autowired` on fields), not constructor injection. - **MyBatis mappers are Java interfaces** with XML mapping files in `src/main/resources/mapper/` - **Dynamic SQL provider**: `ruoyi-system/.../mapper/DynamicSqlProvider.java` provides programmatic SQL for some queries - **Service naming convention**: RuoYi standard services use `ISysXxxService` interface + `SysXxxServiceImpl` implementation. Custom services (added by this project) often omit the `I` prefix and use concrete classes directly. - **TDengine SQL is built with string concatenation** in `TDengineService.java` — be careful with SQL injection if user input reaches those paths. - **MQTT credentials are hardcoded** in `TdengineController2.java` and `TdegnineController3.java`. - **Database passwords are in plaintext** in `application-druid.yml`. - **The project uses Alibaba Maven mirror** (`https://maven.aliyun.com/repository/public`) configured in root `pom.xml`.