Files
workspace/code/fms/CLAUDE.md
T
2026-08-02 22:05:44 +08:00

115 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Repository overview
FMS — a freight-forwarding / logistics management platform (货代物流管理系统). Two-part app:
- `fms-api/` — Spring Boot 4.1 (Java 21, Maven) backend. Serves on port **8088** with context-path **`/api`**. SQL Server via raw JDBC.
- `fms-vue/` — Vue 3 + Vite 8 (pnpm) frontend. Dev server on port **5180** (strict port), proxies `/api` → `http://127.0.0.1:8088`.
Both parts are under active development; `CODEBUDDY.md` at the repo root is the equivalent guidance file for CodeBuddy — keep it in sync when this file changes.
Supporting docs at repo root (Chinese — read them before feature work):
- `开发规范.md` — **binding** development rules (summarized in "Conventions" below).
- `新数据库结构.md` — authoritative `s_module` metadata schema.
- `旧数据库结构.md` — legacy schema, reference only; never modify the legacy DB (`G3HY2025`) unless the user says so.
- `模块管理实施计划.md`, `数据库迁移计划.md`, `文件组件设计方案.md` — feature/migration/file-component plans.
- `docs/refactor/模块管理功能设计.md` — module-management feature design.
## Commands
### Backend (`fms-api/`)
```bash
./mvnw clean package # build jar -> target/fms-api-0.0.1-SNAPSHOT.jar
./mvnw test # all tests (8 test classes under src/test)
./mvnw test -Dtest=ClassName # single test class
./mvnw test -Dtest=ClassName#methodName # single test method
./mvnw spring-boot:run # dev server (or start.cmd, which pins JAVA_HOME/MAVEN_HOME)
```
JWT secret comes from env `FMS_JWT_SECRET` (dev default in `application.yaml`).
### Frontend (`fms-vue/`)
```bash
pnpm install
pnpm dev # vite dev server on :5180
pnpm build # production build
pnpm preview # preview built output
pnpm fmt # format with oxfmt
pnpm fmt:check # verify formatting
```
There is **no lint or test script** in the frontend.
### Build/test discipline (from `开发规范.md`)
Do **not** run builds or start services after finishing a change — only when the user explicitly asks. Running tests to verify changes is allowed (backend `./mvnw test`, frontend `pnpm fmt:check`). When delivering, state whether tests were run and their results, and that no build was executed.
## Backend architecture
The defining trait: **almost no business-specific endpoints.** Nearly all data access flows through a small set of generic endpoints in `DataController` (`/data`), implemented by `DataService` / `DataSaveService`. There are no entity/DAO/mapper layers — `utils/DbUtils` does raw JDBC against SQL Server. All endpoints return `utils/ApiResponse` (`{ success, code, message, data }`; `code: 0` = success).
Generic endpoints (full path includes the `/api` context-path):
- `POST /data/loaddata` — rows from a `view_name` with optional `search_condition` / `order_by` / `search_columns`.
- `POST /data/page` — paginated load (`page_no`, `page_size`, `view_name`, `order_by`); returns `{ rows, row_count, total, page_no, page_size }`.
- `POST /data/saveobjt` — batch upsert across multiple tables in one transaction (`DataSaveService.save`). Body is a JSON array of `{ table, key_field, inserts[], updates[], deletes[] }`; the whole request rolls back if any table fails.
- `GET /data/nextid` — Snowflake ID(s) from `utils/snowflake/idgen/IdGenerator`; `?count=N` returns an array.
- `GET /data/nextcode?moduleId=&count=` — business auto-code from a `s_module_auto_code` rule (`DataSaveService.nextCode`); generates unique codes and updates the rule's counter.
- `POST /data/describe` — column metadata for a table/view.
- `POST /data/loaddatabysql` — runs an arbitrary SQL string. **Avoid**; prefer the structured endpoints.
Key rules:
- Prefer the generic endpoints for query/paging/save/ID/code generation. Do **not** change their URL, parameters, response shape, or logic, and do **not** add new business-specific endpoints or Service classes, unless you first explain why the generic API cannot do it and get user confirmation.
- `bigint` values (e.g. `b_id`) are serialized to the frontend as **strings** via `config/JacksonConfig` (Long → string).
- Queries default to no `search_columns` / `searchColumns` unless restricting fields is required and the fields are confirmed to exist.
### Multi-organization database routing
Connections are per-organization SQL Server Druid pools; the active org is chosen per request:
- `config/dbconfigs/<ORG_ID>.properties` holds `url`/`username`/`password`/`driver` for one org (e.g. `G3HD.properties`). **These files are git-ignored and must not be committed.** The directory is set by `fms.database.config-dir` in `application.yaml` (default `./config/dbconfigs`).
- `database/OrgDatabaseConfigLoader` loads a properties file by org id; `OrgDataSourceManager` caches one `DruidDataSource` per org; `database/OrgRoutingDataSource` picks it per request.
- `config/JwtAuthFilter` parses the JWT and sets `database/OrgContext.orgId`, so `DbUtils` targets the right datasource.
- Feature work operates on the `FMS` database; the legacy `G3HY2025` DB is read-only reference.
### Auth / JWT
- `config/JwtAuthFilter` (skips `/auth/login` and `OPTIONS`) validates `Authorization: Bearer`, checks `service/ActiveSessionRegistry` (single-device enforcement), and sets `OrgContext.orgId`.
- `utils/JwtUtils` (jjwt) carries `orgid` / `sessionId` claims; `config/AuthProperties` reads `fms.auth.jwt-secret` / `fms.auth.jwt-expiration`.
- `POST /auth/login` (`AuthController` / `AuthService`) takes `{ orgid, userid, password }`; `service/LoginLogService` records login events to `s_login_log`.
## Frontend architecture
Vue 3 `<script setup>` SFCs, Pinia for state, **antdv-next** components (auto-imported via `@antdv-next/auto-import-resolver` in `vite.config.js` — do not manually import antdv-next components). There is **no vue-i18n**; i18n is custom and DB-driven from the `b_i18n` table.
### API service layer
`src/services/api.js` (on the axios wrapper `src/services/http.js`, baseURL `/api`) exposes `loginApi`, `loadDataApi`, `pageDataApi`, `saveObjectApi`, `nextIdApi`, `nextCodeApi`, `describeApi`, `loadDataBySqlApi`. Feature code should call these (or wrap them), never introduce new backend URLs from the frontend.
### Routing & permissions
- `src/router/index.js` + `routes.js` register views via **static explicit `import()`**. Data-backed routes carry `meta.moduleId` (a data-module `b_code`, e.g. `m_metadata`, `s_login_log`), plus `title`, `section`, `menuKey`, `requiresAuth`.
- The `beforeEach` guard checks auth, then loads the permission store and calls `canAccess(meta.moduleId)`.
- `src/stores/permissions.js` loads `s_module` / `b_user_module` / `b_user_power` / `s_module_power`, resolves a `moduleId` to its parent **page** module through `s_module` (see `resolvePageId`), and checks the page grant. Account `g3soft` is a super-admin bypass. Permission enforcement is currently **frontend-only**; backend interception is not implemented.
- Other stores: `stores/auth.js` (token/user, persisted), `stores/app.js` (locale default `zh-CN`, sidebar collapse, visited tabs).
### Shared components & utils
- `components/fms-table/FmsTable.vue` — the project table; **all tables must use it** (not a hand-rolled table). Cell renderers live in `components/fms-table/utils/`.
- `components/fms-module-list/` — generic data-module list page (`FmsModuleListPage`, `FmsModuleTable`, `FmsQueryControl`, `FmsQueryToolbar`) built from module config.
- `components/FmsTree.vue`, `FmsSelect.vue`, `FmsAutoComplete.vue`, `FmsMemoInput.vue`, `FmsLoadingOverlay.vue`.
- `utils/dataChanges.js` — clone/diff/merge helpers for `saveobjt` (e.g. `diffRows`, `allocateTemporaryIds`, `remapForeignKeys`, `tableChange`, `hasChanges`); `utils/tempId.js`, `utils/tree.js`, `utils/i18n.js`.
### Module-management feature
`src/views/module-management/index.vue` is a three-panel page (module tree + tabs) over `s_module`. Panels: `components/ModuleBasicPanel.vue`, `ModuleFieldsPanel.vue`, `ModuleListConfigPanel.vue`, `ModuleEditConfigPanel.vue`, `ModuleQueryConfigPanel.vue`, `ModuleAutoCodePanel.vue`, `ModulePowersPanel.vue`, `ModuleI18nPanel.vue`, plus `FieldGroupManager.vue` / `I18nQuickSetModal.vue`. `src/data/moduleDemo.js` is demo data — the real page must not depend on it.
## Database & migrations
- SQL Server; the migration scripts are `fms-api/config/migrations/` (numbered `000`–`026`, plus `verify_business_schema.sql`). They target the `FMS` database, assert the DB name, use `XACT_ABORT ON` + explicit transactions, and are idempotent (re-runnable).
- `config/migrations/tools/` has Java helpers to generate/run/verify business-schema migrations; `数据库迁移计划.md` documents the plan and 2026-07-29 execution results.
- Core metadata tables: `s_module`, `s_module_field`, `s_module_field_group`, `s_module_field_list`, `s_module_field_edit`, `s_module_field_query`, `s_module_power`, `s_module_auto_code`, `b_user_module`, `b_user_power`, `b_i18n`, `b_i18n_type`. `s_module.b_id` is an immutable Snowflake ID; `b_code` is a mutable, unique lowercase code (e.g. `m_metadata`); `b_module_type` is `directory` / `page` / `data`. Follow `新数据库结构.md` exactly.
## Conventions (from `开发规范.md`)
- **Write like the existing code**: mirror the style of the closest existing example (API calls, error handling, state). Prefer `if/else` over ternary/`?.` chains and `for` over `reduce`; avoid gratuitous syntax sugar and pointless abstraction. Readability beats brevity.
- **Performance is the top constraint**, above code convenience and component consistency:
- Table rows render only lightweight native elements (`h("button")`, `<input>`); heavy components (`Dropdown`, `Select`, `Input`, `Modal`) mount **on demand** for the current row and unmount on interaction end — at most one instance at a time. See the on-demand `MoreActions` pattern in `module-management/components/ModulePowersPanel.vue`; the table "⋯" more-actions button must follow it.
- Batch writes go through a single `/data/saveobjt`; no N+1 queries, no remote calls / repeated IO in loops; paginate large data; no `SELECT *`; don't wrap indexed columns in functions or implicitly convert types.
- **No `placeholder` on form inputs**; `label` holds only the field name. Use `FmsTree` without `root-title` (the component owns root titles).
- CSS in `<style scoped lang="scss">`; no `@media`/responsive breakpoints (desktop only). Prefer antdv-next component props/design tokens over custom styles; don't override default component styles unless required and then keep it minimal.
- **Scope**: change only what the current task needs — no unrelated refactors or formatting. Confirm impact before touching generic endpoints, shared components, or global styles. Preserve existing functionality and uncommitted user work.
- Boolean columns are `int` `0/1`; `b_xh` is display order (smaller = first). Table/column names must be backend-validated identifiers — never concatenated into SQL.