8.6 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
See also
AGENTS.mdfor module breakdown, framework choices, conventions, and gotchas.
Project overview
weather-data is a full-stack meteorological data analysis platform: Java 17 / Spring Boot 3.5 multi-module backend + Vue 3 / Vite 5 / TypeScript frontend.
weather-data/
├── system-common/ → shared Java lib
├── system-admin/ → admin backend (port 8080, /system-admin)
├── system-api/ → external API service (port 8081)
├── system-dynamic-datasource → multi-DS support (placeholder)
├── renren-generator/ → code generator
└── weather-data-ui/ → Vue 3 SPA frontend
Database: weather_data_system (MySQL). Init from system-admin/db/mysql.sql. Default admin: admin / admin.
Backend
Build & Run
mvn clean install -DskipTests # full build (tests skipped by default)
mvn clean install -DskipTests=false # build with tests
mvn -pl system-admin -DskipTests=false -Dtest=YourTestClass test # single test
Launch from IntelliJ:
AdminApplication(system-admin/) → port 8080, context/system-adminApiApplication(system-api/) → port 8081GeneratorApplication(renren-generator/)
Service layer pattern
Two base classes in system-common:
| Base | Purpose |
|---|---|
CrudService<Dao, Entity, DTO> |
Generic CRUD: page(), get(), save(), update(), delete() |
BaseService<Dao> |
Lighter base without DTO generic |
Module convention:
modules/<name>/
├── controller/ → @RestController, returns Result
├── dao/ → extends BaseMapper<Entity> (MyBatis-Plus)
├── dto/ → request/query DTOs (often extends BaseEntity)
├── entity/ → @TableName JPA entity
├── service/ → interface extends CrudService/BaseService
│ └── impl/ → @Service implementation
├── excel/ → EasyExcel VO classes (optional)
└── vo/ → response VO classes (optional)
Mapper XMLs: src/main/resources/mapper/<domain>/**/*.xml
Key cross-cutting mechanisms
| Mechanism | How |
|---|---|
| Data permissions | @DataFilter on controller → DataFilterAspect → MyBatis interceptor injects dept-based SQL |
| Auto-fill | FieldMetaObjectHandler fills creator/date via MyBatis-Plus |
| Scheduled jobs | Quartz. schedule_job table, implements ITask, @Component("beanName") |
| File scanning | WatchService (primary) + Quartz fallback (FileScanTask) + startup runner |
| Excel import | EasyExcel + async progress via WeatherDataImportManager |
| API responses | Always wrapped in Result |
| Validation | Hibernate Validator. XSS filter via XssFilter |
PK & Auth
- PK:
ASSIGN_ID(Snowflake). All entities extendBaseEntity. - Auth: Apache Shiro 1.12 (Jakarta) + OAuth2 token. Login →
tokenheader. - API module:
@Loginannotation +AuthorizationInterceptor.
Redis & Docs
- Redis: optional,
project-options.redis.open(defaultfalsein dev).RedisAspect. - API docs: Knife4j at
/doc.html, disabled by default (knife4j.enable: false).
Weather domain (backend)
Three sub-modules under system-admin/.../modules/weather/:
| Module | Purpose |
|---|---|
dailydata/ |
Daily observations, Excel batch import (async), EasyExcel listener |
station/ |
Weather station CRUD, linked to dept via dept_id |
filescan/ |
File monitoring + serving. Format: <地区>地区-<指标>.png / <地区>地区631信息.txt |
Parameter scan_root_path in sys_params controls file-scan base directory.
Frontend (weather-data-ui/)
Commands
npm install # install dependencies
npm run dev # Vite dev server
npm run build / npm run build:prod # production build
npm run serve # preview production build
npm run lint # lint with autofix
npx vue-tsc --noEmit # type-check
Stack
- Vite 5 + Vue 3 + TypeScript SPA
- Element Plus + Element Plus Icons for UI
vue-routerwith hash history- Pinia for state management
- Axios via
src/utils/http.ts+src/service/baseService.ts - API base URL:
VITE_APP_APIenv var, overridable at runtime bywindow.SITE_CONFIG.apiURL
Routing & state
src/router/base.ts: base routes (/,/home,/login, etc.)src/router/index.ts:beforeEachguard — auth check, dynamic route registration from backend menus, tab managementsrc/store/index.ts(useAppStore): user, permissions, dicts, dynamic routes, tabssrc/store/importTasks.ts: long-running import task state for header indicatorsrc/utils/router.ts: converts backend menu records → Vue router records, flattens nested routes for keep-alive
Layout is event-driven: src/layout/ shell + mitt event bus (src/utils/emits.ts). Trace both the Pinia store and mitt events when changing navigation/sidebar/tabs/theme.
Weather frontend module
The home dashboard (src/views/home.vue) uses a composable-based architecture. All domain logic is extracted from the SFC into src/composables/:
| Composable | Responsibility |
|---|---|
useWeatherConstants.ts |
Rain levels, temperature thresholds, filter field definitions, fmtVal(), level/class helpers |
useWeatherFilter.ts |
Filter state, toggle/reset/match logic, matchOp() |
useWeatherStats.ts |
computeStats(), buildStatCards(), buildSummary(), rainLevelDistribution, WeatherDataRow type |
useWeatherChart.ts |
ECharts dynamic import, buildChartOption(), ResizeObserver, precise trigger key (not deep watch) |
useWeatherExport.ts |
PNG/PDF export with dynamic html2canvas/jspdf imports, loading indicator |
Supporting utils:
src/utils/chartBuilder.ts— chart option builderssrc/utils/exportReport.ts— sharedexportPNG()/exportPDF()
Critical rules learned (must follow)
1. Null ≠ zero — missing data MUST be preserved as null
When mapping backend API responses to frontend models, never default missing numeric values to 0. Rainfall of 0mm means "no rain that day" (valid measurement); null means "no data available" (missing record). Use : null not : 0 in data mapping, and display "—" for null values via fmtVal().
// ✅ Correct
rainfall: row.rain2020 != null ? +row.rain2020 : null,
// ❌ Wrong — confuses "no data" with "measured zero"
rainfall: row.rain2020 != null ? +row.rain2020 : 0,
All helper functions (rainLevelLabel, tmaxValClass, tminValClass, rainValClass) must accept number | null and return "—" or "" for null. Stats computations (computeStats, fStats, filterExtremes) must skip null values in sums and extreme comparisons. ECharts will naturally render null as gaps in line/bar series.
2. Heavy libraries must use dynamic imports
html2canvas, jspdf, and echarts are NOT imported at module level. They are loaded via await import() only when the user triggers export or chart rendering. This keeps them out of the initial bundle (~600KB saved).
3. Google Fonts go in index.html, not scoped styles
Never use @import url("https://fonts.googleapis.com/...") inside Vue scoped styles — it blocks rendering. Instead, add <link rel="preconnect"> + <link rel="stylesheet"> in index.html.
4. Export must show user feedback
When exporting images/PDFs, always show a loading indicator (ElLoading.service fullscreen) and a success/failure message (ElMessage). Disable the export button during rendering to prevent double-clicks.
5. Deep watchers on filter objects are banned
Never use watch(filters, callback, { deep: true }). Instead, derive a precise computed trigger key that only includes fields actually affecting the output (e.g., dataHash, filteredHash, extremesVersion) and watch that.
Common page pattern
Admin CRUD pages use src/hooks/useView.ts for shared list-page workflow: query, paging, sorting, delete, export, permission checks, dictionary lookup. Check whether behavior comes from useView before refactoring these screens.
Other conventions
- Reusable selector/tree controls:
src/components/sys-*, registered globally inmain.ts - SVG icons:
vite-plugin-svg-iconsfromsrc/assets/icons/svg/ - Tests: no test runner configured yet
Repository notes
README.mddoes not contain substantive guidance; operational context lives in this file andAGENTS.md.weather-data-ui/CLAUDE.mdis superseded by this merged file — the rootCLAUDE.mdcovers both frontend and backend.