The runtime: BusinessBaseController & friends
xlyEntry/src/main/java/com/xly/web/businessweb/ is where the
metadata-driven runtime lives. This page is the maintainer's map of the
controllers and services that carry most of the generic form runtime.
The load-bearing controllers
| Class | Package | Role | Most-cited endpoints |
|---|---|---|---|
BusinessBaseController |
web/businessweb/ |
Universal CRUD for metadata-driven modules. The default API surface for every form. |
/business/getModelBysId/{moduleId}, /business/getBusinessDataByFormcustomId/{formId}, /business/addUpdateDelBusinessData, /business/getSelectDataBysControlId/{controlId}
|
BusinessConfigformController |
web/businessweb/ |
Per-user / per-group display customization for an existing form, not the base form-definition CRUD. |
/configform/getConfigformData/{moduleId}, /configform/sHandleConfigform, /configform/sCopyConfigform
|
GdsmoduleController |
web/systemweb/ |
Module-tree and module-definition CRUD used by the builder side. |
/gdsmodule/getModuleTreePro, /gdsmodule/addGdsmodule, /gdsmodule/updateGdsmodule
|
GdsconfigformController |
web/systemweb/ |
Form-master and form-slave metadata CRUD. | endpoints under /gdsconfigform/*
|
GdsconfigtbController |
web/systemweb/ |
Virtual-table master/slave metadata CRUD. | endpoints under /gdsconfigtb/*
|
BusinessTreeGridController |
web/businessweb/ |
Tree-grid endpoints. In this branch, the proc-backed path is implemented; the plain getTreeGrid service method is still a stub. |
/treegrid/getTreeGridByPro/{formId} |
GenericProcedureCallController |
web/businessweb/ |
Generic stored-procedure invocation by name + parameters. | /procedureCall/doGenericProcedureCall |
ConfigformPanelController |
web/businessweb/ |
Panel-layout persistence in gdsconfigformpanel. |
/panel/get/{sFormId}, /panel/save/{sFormId}
|
CheckFlowController |
web/businessweb/ |
Activiti workflow surface (approve / reject / view) — only meaningful when workflow is deployed. | endpoints under /checkFlow/*
|
Note that the controllers split across two packages: businessweb/
hosts the runtime-facing endpoints, while systemweb/ hosts the
metadata-CRUD endpoints used by the builder side. Both compile into the
same xlyEntry WAR.
The four-table read
For any metadata-driven module, the request lifecycle (see Concepts → request lifecycle) boils down to:
public Map<String, Object> getModelBysId(Map<String, Object> map) {
List<Map<String, Object>> formList = this.getModelConfigByModleId(map); // 1. join gdsmodule⋈form-master⋈form-slave
List<Map<String, Object>> fList = businessGdsconfigformsService.getFormconstData(qMap); // 2. gdsformconst
List<Map<String, Object>> jList = businessGdsconfigformsService.getJurisdictionData(qMap); // 3. gdsjurisdiction (skipped for ADMIN)
Map<String, Object> billnosettingMap = businessGdsconfigformsService.getBillnosettingData(param); // 4. sysbillnosettings
List<Map<String, Object>> reportList = printReportService.getReportData(qMap); // 5. sysreport
return composite(formList, fList, jList, billnosettingMap, reportList);
}
— BusinessBaseServiceImpl.java. Read this method first; the
rest of the runtime is variations on it.
The five-key composite returned
| Key | Source | Frontend uses for |
|---|---|---|
formData |
gdsmodule ⋈ gdsconfigformmaster ⋈ gdsconfigformslave (+ overlays) |
Form layout |
gdsformconst |
gdsformconst |
Form-level constants, dropdown labels |
gdsjurisdiction |
gdsjurisdiction |
Per-button / per-data permissions |
billnosetting |
sysbillnosettings |
Document numbering rules |
report |
sysreport |
Print templates |
The save endpoint
POST /business/addUpdateDelBusinessData bundles add+update+delete in
one transactional batch. The frontend supplies sTable and a column
map per row, in this shape:
{
"addData": [{"sTable": "<table>", "column": {"sId": "", ...}}],
"updateData": [{"sTable": "<table>", "column": {"sId": "", ...}}],
"delData": [{"sTable": "<table>", "column": {"sId": "", ...}}]
}
When gdsmodule.sSaveProName is empty, the framework's default
Add/Update path runs — AddDelUpdCommonServiceImpl.java. When it's
populated, the named stored proc is invoked instead.
Multi-tenant boundary
Every controller method's first non-trivial line is:
RequestAddParamUtil.me().addParams(params, userInfo);
— xlyPersist/.../RequestAddParamUtil.java, 56 lines (xlyApi has its
own near-identical 57-line copy at xlyApi/.../api/util/RequestAddParamUtil.java).
This is the single point where the user's session identity (sBrandsId,
sSubsidiaryId, sBrId, sSuId, sLoginId, sLanguage, sTeamId,
sMachineId, plus eight more — sixteen keys total) is injected into the
downstream params map. Every MyBatis query and stored-procedure call
that references #{sBrandsId}/#{sSubsidiaryId} is automatically scoped
from there.
A new controller method that doesn't call RequestAddParamUtil is a
multi-tenant bug.
Security concerns to audit
Two flagged in slices that belong here permanently:
-
sTablevalidation inaddUpdateDelBusinessData. The frontend names the target table directly. The runtime must cross-check that the supplied table is one of the form's authorised backing tables, or it's a privilege-escalation surface. Confirm the check exists; if it doesn't, raise it as a security ticket. Tracked in the Slice 1 v2 follow-up. -
ADMIN bypasses permissions.
BusinessBaseServiceImplskips thegdsjurisdictionload entirely forUserType.ADMIN. ADMIN account governance must come from outside the app.
Cache invalidation
When BACK saves a metadata change, a JMS message fires and
ConsumerChangeGdsModuleThread (in xlyErpJmsConsumer) clears the
cached metadata on every running node. See cache invalidation on
metadata change.