weather-data/CLAUDE.md

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.md for 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-admin
  • ApiApplication (system-api/) → port 8081
  • GeneratorApplication (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 extend BaseEntity.
  • Auth: Apache Shiro 1.12 (Jakarta) + OAuth2 token. Login → token header.
  • API module: @Login annotation + AuthorizationInterceptor.

Redis & Docs

  • Redis: optional, project-options.redis.open (default false in 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-router with hash history
  • Pinia for state management
  • Axios via src/utils/http.ts + src/service/baseService.ts
  • API base URL: VITE_APP_API env var, overridable at runtime by window.SITE_CONFIG.apiURL

Routing & state

  • src/router/base.ts: base routes (/, /home, /login, etc.)
  • src/router/index.ts: beforeEach guard — auth check, dynamic route registration from backend menus, tab management
  • src/store/index.ts (useAppStore): user, permissions, dicts, dynamic routes, tabs
  • src/store/importTasks.ts: long-running import task state for header indicator
  • src/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 builders
  • src/utils/exportReport.ts — shared exportPNG()/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 in main.ts
  • SVG icons: vite-plugin-svg-icons from src/assets/icons/svg/
  • Tests: no test runner configured yet

Repository notes

  • README.md does not contain substantive guidance; operational context lives in this file and AGENTS.md.
  • weather-data-ui/CLAUDE.md is superseded by this merged file — the root CLAUDE.md covers both frontend and backend.