11 KiB
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— authoritatives_modulemetadata 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/)
./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/)
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 aview_namewith optionalsearch_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) fromutils/snowflake/idgen/IdGenerator;?count=Nreturns an array.GET /data/nextcode?moduleId=&count=— business auto-code from as_module_auto_coderule (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.
bigintvalues (e.g.b_id) are serialized to the frontend as strings viaconfig/JacksonConfig(Long → string).- Queries default to no
search_columns/searchColumnsunless 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>.propertiesholdsurl/username/password/driverfor one org (e.g.G3HD.properties). These files are git-ignored and must not be committed. The directory is set byfms.database.config-dirinapplication.yaml(default./config/dbconfigs).database/OrgDatabaseConfigLoaderloads a properties file by org id;OrgDataSourceManagercaches oneDruidDataSourceper org;database/OrgRoutingDataSourcepicks it per request.config/JwtAuthFilterparses the JWT and setsdatabase/OrgContext.orgId, soDbUtilstargets the right datasource.- Feature work operates on the
FMSdatabase; the legacyG3HY2025DB is read-only reference.
Auth / JWT
config/JwtAuthFilter(skips/auth/loginandOPTIONS) validatesAuthorization: Bearer, checksservice/ActiveSessionRegistry(single-device enforcement), and setsOrgContext.orgId.utils/JwtUtils(jjwt) carriesorgid/sessionIdclaims;config/AuthPropertiesreadsfms.auth.jwt-secret/fms.auth.jwt-expiration.POST /auth/login(AuthController/AuthService) takes{ orgid, userid, password };service/LoginLogServicerecords login events tos_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/pageComponents.jsis the explicit hand-written page component whitelist;routes.jsregisters only keys from that registry.AppSidebarreadsv_s_function_node. The router guard authorizes the matching FunctionNode route.stores/permissions.jsreads the effective FunctionNode, PageNode and Action permission views. Roles provide primary grants; direct user grants are additive. Super access comes fromb_role.b_is_super, never from an account-name bypass.- Permission enforcement is currently frontend-only; backend data-scope and write interception remain reserved.
- Other stores:
stores/auth.js(token/user, persisted),stores/app.js(locale defaultzh-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 incomponents/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 forsaveobjt(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 only coordinates three maintenance areas: FunctionNode structure, PageNode composition, and Module resources. State and persistence live in the three composables/; independent panels own editing UI. Resource configuration is saved through one /data/saveobjt transaction.
Database & migrations
- SQL Server; the migration scripts are under
fms-api/config/migrations/. Module cutover uses033_create_module_structure.sql, then034_module_runtime_views.sql, then the read-onlyverify_module_architecture.sql. The cutover is one-time and intentionally removes the old metadata model. config/migrations/tools/has Java helpers to generate/run/verify business-schema migrations;数据库迁移计划.mddocuments the plan and 2026-07-29 execution results.- Core architecture is
s_moduleplus type tables,s_function_node,s_page_node,s_module_profile, capability tables, and explicit role/user grant tables. IDs and technical codes are immutable. Follow新数据库结构.mdexactly.
Conventions (from 开发规范.md)
- Write like the existing code: mirror the style of the closest existing example (API calls, error handling, state). Prefer
if/elseover ternary/?.chains andforoverreduce; 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-demandMoreActionspattern inmodule-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; noSELECT *; don't wrap indexed columns in functions or implicitly convert types.
- Table rows render only lightweight native elements (
- No
placeholderon form inputs;labelholds only the field name. UseFmsTreewithoutroot-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
int0/1;b_xhis display order (smaller = first). Table/column names must be backend-validated identifiers — never concatenated into SQL.