20260802220544

This commit is contained in:
oneao committed 2026-08-02 22:05:44 +08:00
1 parent c00329b5a4
commit 4fc9040a8a
60 files changed
+6273 -908

No files matched your search

+114
View File
@@ -0,0 +1,114 @@
# 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.