7.9 KiB
CODEBUDDY.md
This file provides guidance to CodeBuddy Code when working with code in this repository.
Repository layout
This is a two-part application (FMS — 模块/数据管理平台):
fms-api/— Spring Boot 4.1 (Java 21, Maven) backend. Serves on port 8088 with context-path/api.fms-vue/— Vue 3 + Vite 8 (pnpm) frontend. Dev server on port 5180, proxies/api→http://127.0.0.1:8088.
Supporting docs in the repo root (Chinese):
开发规范.md— binding development rules (read it; summarized below).新数据库结构.md— the module-management table design (authoritative schema).旧数据库结构.md— legacy schema, kept for reference only.模块管理实施计划.md— phased plan for the module-management feature.fms-api/config/migrations/— SQL Server schema migration scripts.
Commands
Backend (fms-api/)
./mvnw clean package # build jar -> target/fms-api-0.0.1-SNAPSHOT.jar
./mvnw test # run all tests (8 test classes under src/test)
./mvnw test -Dtest=ClassName # run a single test class
./mvnw test -Dtest=ClassName#methodName # run a single test method
./mvnw spring-boot:run # run the dev server (or use start.cmd)
java -jar target/fms-api-0.0.1-SNAPSHOT.jar # run built jar
JWT secret is read from env FMS_JWT_SECRET (defaults to a dev value 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 # check formatting
There is no lint or test script in the frontend.
Build/test discipline (from 开发规范.md)
Do not run build/test/start yourself after finishing a change. Only build, test, or start services when the user explicitly asks. When delivering, state that build/test were not executed.
Backend architecture
The defining characteristic of this backend is that it has almost no business-specific endpoints. Almost all data access goes through a small set of generic endpoints in DataController, implemented by DataService / DataSaveService. There are no entity/DAO/mapper layers — utils/DbUtils does raw JDBC against SQL Server.
Generic data endpoints (full path includes the /api context-path):
POST /api/data/loaddata— load rows from aview_namewith optional search/order (DataService.loadData).POST /api/data/page— paginated load (page_no,page_size,view_name,order_by).POST /api/data/saveobjt— batch upsert across multiple tables in one transaction (DataSaveService.save). Request body is a JSON array of{ table, key_field, inserts[], updates[], deletes[] }; the whole request rolls back if any table fails.GET /api/data/nextid— Snowflake ID fromutils/snowflake/IdGenerator.POST /api/data/loaddatabysql— runs an arbitrary SQL string. Avoid this; it exists but new feature work should prefer the structured endpoints.POST /api/auth/login—AuthController/AuthService.
Key rules when working on the backend:
- Prefer the generic endpoints for query/paging/save/ID-generation. Do not modify their URL, parameters, response shape, or logic, and do not add new business-specific endpoints, unless you first explain why the generic API cannot do it and get user confirmation.
b_idvalues that arebigintsnowflake IDs must be serialized to the frontend as strings.
Multi-organization database routing
Connections are per-organization SQL Server pools (Druid). The active org is selected at request time:
config/dbconfigs/<ORG_ID>.propertiesholdsurl/username/password/driverfor one org (e.g.g3hd.propertiesfor orgG3HD). These files are git-ignored and must not be committed. Theconfig-diris set byfms.database.config-dirinapplication.yaml(default./config/dbconfigs).database/OrgDatabaseConfigLoaderloads a properties file by org id;OrgDataSourceManagercaches oneDruidDataSourceper org.config/JwtAuthFilterparses the JWT, thendatabase/OrgContextholds the currentorgIdsoDbUtilstargets the right datasource (OrgRoutingDataSource).- New features operate on the
g3hd.propertiesdatabase (fms); do not touch the legacy database.
Auth / JWT
config/JwtAuthFilter(skips/auth/login) validatesAuthorization: Bearer, checksActiveSessionRegistry(single-device enforcement), and setsOrgContext.orgId.utils/JwtUtils(jjwt) putsorgid/sessionIdclaims.config/AuthPropertiesreadsfms.auth.jwt-secret/fms.auth.jwt-expiration.- Login response returns
id,account,name; the JWT session identifier stays the account.
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 that the resolver handles). There is no vue-i18n; i18n is custom and DB-driven.
API service layer
src/services/api.js (built on axios wrapper src/services/http.js, baseURL /api) exposes:
loadDataApi→POST /data/loaddatapageDataApi→POST /data/pagesaveObjectApi→POST /data/saveobjtnextIdApi→GET /data/nextidloadDataBySqlApi→POST /data/loaddatabysql(avoid)loginApi→POST /auth/login
Feature code should call these, or wrap them in a dedicated service like src/services/moduleManagement.js (e.g. loadModuleConfiguration, nextIds, saveChanges). Do not introduce new backend URLs from the frontend — reuse the generic endpoints.
Routing & permissions
src/router/index.jsregisters view components via static explicitimport(), not a dynamic whitelist. Each route carriesmeta.moduleCode(e.g.m_module), plusmenuKey,title,requiresAuth.- A
beforeEachguard checks auth, thenpermissionStore.canModule(to.meta.moduleCode).src/stores/permissions.jsloadsb_user_module/b_user_power/b_module_powerand gates page entry and button enable/disable. Currently permission enforcement is frontend-only; backend interception is not yet implemented. src/layouts/components/AppSidebar.vuestill uses a hardcodedfallbackMenuItemsarray. It should fall back to this static menu when the generic-interface module load fails.
State & i18n
- Pinia stores:
stores/auth.js(token/user, persisted),stores/app.js(locale, sidebar collapse, visited tabs),stores/permissions.js(module/power grants). - i18n:
appstore holdslocale(defaultzh-CN); translation text comes from the DBb_i18ntable (loaded vialoadModuleConfiguration), not vue-i18n.
Module management feature
src/views/module-management/ holds ModuleManagementView.vue plus components/ panels (ModuleTreePanel, ModuleBasicInfoPanel, ModuleFieldDefinitionPanel, ModulePowerPanel, ModuleTranslationsPanel). Demo data lives in src/data/moduleDemo.js — the real page must not depend on it. The table design (six tables: b_module, b_module_field, b_module_power, b_user_module, b_user_power, b_i18n) is in 新数据库结构.md; follow that schema exactly. b_module.b_id is an immutable snowflake ID; b_module.b_code is a mutable, unique lowercase code (e.g. m_biz). Bigint IDs are returned to the frontend as strings.
Cross-cutting conventions
- Reuse existing components and antdv-next; extract shared logic into components/composables/services only when it genuinely reduces duplication. Keep new CSS scoped to the component.
- Changes should stay scoped to the current task — no unrelated refactors or formatting. Confirm impact before touching generic endpoints, shared components, or global styles. Preserve existing functionality and the user's uncommitted work.
- Boolean columns are
int0/1;b_xhis display order (smaller = first). Table/column names must be backend-validated identifiers — never concatenated into SQL.