# Internal API (`xlyEntry`) The `xlyEntry` service hosts the SPA's runtime API. It is the largest of the three tiers — its controllers compile into the same WAR as the framework's metadata-driven runtime, and most calls hit one of a handful of universal endpoints that read or write any module. This API is **not a stable contract for external callers**. Endpoint shapes change as the framework changes. External integrations belong on the [External API](external.md). This page exists for maintainers and SPA-extension authors. For the request-lifecycle and code-level walkthrough, see the [Maintainer runtime chapter](../reference/maintainer/runtime.md). This page is the catalog of HTTP entry points. ## The universal CRUD surface — `/business/*` | Endpoint | Method | Purpose | |---|---|---| | `/business/getModelBysId/{sModelsId}` | GET | Returns the form layout for a module — the five-key composite (`formData`, `gdsformconst`, `gdsjurisdiction`, `billnosetting`, `report`). | | `/business/getBusinessDataByFormcustomId/{gdsconfigformmasterId}` | POST | Returns rows of business data for a form, paginated. Branches to `getBusinessDataByGroup` when `sGroupList` is set. | | `/business/getBusinessDataByIndex` | POST | First / last / next / previous-record navigation. | | `/business/addBusinessData` | POST | Single insert. | | `/business/addUpdateDelBusinessData` | POST | Bundled add+update+delete in one transactional call. The frontend names the target table directly via `sTable`. | | `/business/getSelectDataBysControlId/{sId}` | POST | Dropdown population for a single control, by control `sId`. | | `/business/getSelectLimit/{sId}` | POST | Paginated variant of the dropdown call. | | `/business/addSysLocking` | POST | Optimistic-lock acquisition when a user starts editing a document — inserts a row in the system lock table keyed by `(sFormGuid, sUserId)`. The SPA fires this when entering edit mode so concurrent editors get a conflict warning. Handler: `BusinessBaseController.java:400-407`. | | `/business/doExamine` | POST | Simple "approve" — flips `bCheck = 1` on the named row via SQL. **Does NOT invoke Activiti**; this is xly's lightweight approval path used by every module that doesn't need multi-step workflow. Handler: `BusinessBaseController.java:384-391` → `BusinessBaseServiceImpl.doExamine` → `ExamineServiceImpl`. See [How xly handles workflow without Activiti](../reference/maintainer/activiti.md#how-xly-handles-workflow-without-activiti) for when Activiti is used instead. | | `/business/getProData` | POST | Generic stored-procedure invocation for a module — alternate path to `/procedureCall/doGenericProcedureCall`. Handler: `BusinessBaseController.java:350-358` → `BusinessBaseServiceImpl.getProData`. Used by FROUNT for module-level proc reads (the home dashboard `/getProData?sModelsId=...&sName=` pattern). | These endpoints are documented in detail by [Slice 1](../slices/01-hello-world.md) (`getModelBysId` + grid load + save) and [Slice 3](../slices/03-report.md) (the view-backed read variant). The handler classes are in `xlyEntry/src/main/java/com/xly/web/businessweb/`. ## Metadata-management endpoints For builder-side actions (creating modules, defining forms, declaring virtual tables) there is a parallel surface in `xlyEntry/src/main/java/com/xly/web/systemweb/`: | Endpoint root | Controller | Purpose | |---|---|---| | `/gdsmodule/*` | `GdsmoduleController` | Module-tree CRUD, including `getModuleTreePro`, `addGdsmodule`, `updateGdsmodule`. | | `/gdsconfigform/*` | `GdsconfigformController` | Form-master and form-slave metadata CRUD. | | `/gdsconfigtb/*` | `GdsconfigtbController` | Virtual-table master/slave metadata CRUD. | ## Specialised runtime endpoints | Endpoint root | Controller | Purpose | |---|---|---| | `/configform/*` | `BusinessConfigformController` | Per-user / per-group display customization. | | `/treegrid/*` | `BusinessTreeGridController` | Tree-grid endpoints (the proc-backed path is implemented in this branch). | | `/procedureCall/*` | `GenericProcedureCallController` | Generic stored-procedure invocation by name + parameters — see [generic procedure dispatch](../reference/maintainer/proc-dispatch.md). | | `/panel/*` | `ConfigformPanelController` | Panel-layout persistence in `gdsconfigformpanel`. | | `/checkflow/*` | `CheckFlowController` | **Empty shell — returns 404.** The class declares the prefix but has zero handler methods. The actual workflow approve/reject/complete URLs come from xlyFlow's `CurrencyFlowController` (served at xlyEntry's context-path because xlyFlow is a library dep): `/currencyFlow/complete/{taskId}/{sBrandsId}/{sSubsidiaryId}/{sUserId}`, `/currencyFlow/completeerp/{sBrandsId}/{sSubsidiaryId}/{sUserName}`, plus `/modeler/*` (root-mapped) for the BPMN modeler. See [Activiti integration](../reference/maintainer/activiti.md). | | `/modelCenter/getModelCenter`, `/modelCenter/getModelCenterCalculation` | `BusinessModelCenterController` | The FROUNT home-page **KPI Work Center** card (titled `KPI监控`). Aggregates open tasks across modules tagged `gdsmodule.bUnTask=1`, partitioned by role and business flow. **Not Activiti-driven.** See [The KPI Work Center](../reference/maintainer/runtime.md#the-kpi-work-center-front-end-home-dashboard). | ## Reporting and printing The print surface lives under `xlyEntry/src/main/java/com/xly/web/report/`: - `PrintReportController` — current jxls / iText print path. - `PrintReportControllerOld.java` — file exists but its class body is fully commented out (and the commented-out class inside is named `PrintReportController`, not `*Old`). It is dead source kept for reference, not an active controller. The frontend's "打印" / "导出" buttons hit these controllers, which load a template from `sysreport`, run the matching view-backed query, and stream a binary file back. See [Slice 3](../slices/03-report.md#6-printable-reports-when-present) for the flow. ## Authentication Every controller method that participates in business data is annotated with `@Authorization` and receives a resolved `UserInfo` via `@CurrentUser`. The session-to-`UserInfo` mapping is the framework's own (cookie + Redis-backed session); see [the multi-tenancy concept page](../concepts/multi-tenancy.md). A request that reaches a controller without authentication does not get past `@Authorization`; if it does (e.g., an unannotated method), that method also bypasses the universal tenant injection in `RequestAddParamUtil` — and is therefore a multi-tenant bug. ## BACK builder sidebar (admin surface) The 10 top-level items in the BACK admin sidebar (login `admin`/`123`, edition `基础版/8s`) — each is a metadata-driven screen wired through the framework primitives above: | Sidebar | URL fragment | Backing form-master `sTbName` | Owning service | |---|---|---|---| | 系统模块配置 (System Module Config) | `/xtmkpz` | `gdsmodule` | `GdsmoduleServiceImpl` | | 数据表内容配置 (Virtual Table Config) | n/a | `gdsconfigtbmaster`/`slave` | `GdsconfigtbServiceImpl` | | 界面显示内容配置 (Form Definition + Customization) | n/a | `gdsconfigformmaster`/`slave`/`customslave`/`personalize` | `GdsconfigformServiceImpl` | | 接口自定义配置 (API Definition) | `/sjbnrpz` | `sysapi` family | (xlyApi-side admin) | | 系统常量配置 (System Constants) | `/xtclpz` | `gdsformconst` | `GdsformconstServiceImpl` — Slice 1 anchor | | 系统权限配置 (Permission Catalog) | n/a | `gdsjurisdiction` | `GdsjurisdictionServiceImpl` | | 常用操作配置 (Common Operations / Button Groups) | n/a | (no entry in `gdsconfigformmaster` for this dev DB; the page is rendered as an admin special-case wired directly in the SPA — when extended via metadata, the data lives under the user-defined button-group tier) | n/a | | 用户信息配置 (User Info) | n/a | `sftlogininfo` family | `GdslogininfoServiceImpl` | | Mysql脚本配置 (SQL Script Authoring) | n/a | (BACK editor over the [`templesql/` scaffolds](../reference/maintainer/sql-templates.md)) | `SqlScriptsServiceImpl` | | 图表配置 (Chart Config) | (no `gdsroute` entry; navigated via SPA state) | `gdsconfigcharmaster`/`slave` | `GdsconfigformServiceImpl` (chart subset) | 8 of 10 are framework primitives covered elsewhere in this catalog + the [Maintainer Reference](../reference/maintainer/management-services.md). **`常用操作配置` is a SPA-side admin special-case** — it appears in the sidebar without a corresponding `gdsconfigformmaster` row in the dev DB, suggesting the page is hardcoded in BACK rather than metadata- driven. **`图表配置`** is fully metadata-driven via two `gdsconfigformmaster` rows pointing at `gdsconfigcharmaster` and `gdsconfigcharslave`; chart definitions there are consumed by xly's dashboard rendering elsewhere in the SPA. ## Beyond the framework primitives — the rest of xlyEntry's surface `xlyEntry` hosts **70 controllers** in total. The 18 framework-side controllers — the universal-runtime ones enumerated explicitly above plus the seven `systemweb/` admin controllers backing the [BACK builder sidebar](#back-builder-sidebar-admin-surface) (`GdsformconstController`, `GdsjurisdictionController`, `GdslogininfoController`, `GdsparameterController`, `LicenseController`, `LoginController`, `SysbrandsController`) — are where every metadata-driven form's lifecycle lives. The remaining 52 controllers exist because the framework's universal CRUD + procedure-dispatch path **wasn't enough** for the use case. Each one is, in effect, a marker for where the data-driven thesis stopped scaling. That makes them worth enumerating even though they are out of the framework's catalogued surface: they show what xly hardcodes in Java versus what it leaves to metadata. A maintainer reading them gets a direct read on the framework's escape-hatch shape. > **Namespace overlap.** `BusinessBaseController` (`/business/*`) and > `QuoquotationController` (also `@RequestMapping("/business")`) share > the URL prefix. Spring resolves by method-level path, so > `/business/addQuotationsheet` and `/business/getQuoquotationProgress` > live on `QuoquotationController` while every other `/business/*` > endpoint is on `BusinessBaseController`. The collision is benign > (no method-path overlap) but the convention is a foot-gun: a > future contributor adding `@PostMapping("/addQuotationsheet")` to > `BusinessBaseController` would silently shadow the quotation path. ### Form-helper and SPA-extension controllers (22) Framework-adjacent endpoints — extensions to the universal CRUD path that didn't fit the form-master / form-slave shape. Most are called *alongside* `/business/*` from the same SPA screen. | Endpoint root | Controller | Role | |---|---|---| | `/bill/*` | `BillController` | Copy-a-document operations (`billCopyToCheck`, `billCopyToCheckWork`) — clone a master+slaves row-set into a new document. Not in `/business/*` because the SPA needs the new `sId` set returned in a single round-trip, which the universal save endpoint doesn't shape. | | `/change/*` | `ChangeController` | Generic field-change recompute (`changeParam`) — the SPA fires this when a field's value triggers a derived recomputation that requires hitting a proc. | | `/parameter/*` | `BusinessParameterController` | Per-module parameter reads. | | `/treeclassify/*` | `BusinessTreeClassifyController` | Tree-grouped variant of the form data load — `/treeclassify/getTreeClassify/{gdsconfigformmasterId}`. Lives outside `/business/*` because the response shape is a nested tree, not a flat row-set. | | `/calcprocedure/*` | `CalcProcedureController` | The runtime side of the [calculation-formula](../reference/builder/define-vtable.md) feature — `/calc` invokes a named calc proc. | | `/calculationFormula/*` | `CalculationFormulaController` | Builder-side metadata for calculation formulas. | | `/calculationStd/*` | `CalculationStdController` | Standard-calc lookup catalog. | | `/char/*` | `CharController` | Chart-config CRUD for the BACK 图表配置 page — wraps `gdsconfigcharmaster`/`slave`. | | `/checkModel/*` | `CheckmodelController` | Approval-model membership reads (`getUserListByModelId/{sCheckModeId}`). Used by the lightweight (non-Activiti) approval flow. | | `/comparatorTree/*` | `ComparatorTreeController` | Comparator-tree reads for filterable hierarchical pickers. | | `/excel/*` | `ExcelController` | Excel **export** of a grid — `/export/{gdsconfigformmasterId}`. Sibling to print, but data instead of report layout. | | `/import/*` | `ImportExcelController` | Excel **import** (`/checkExcel`, then commit). Validates against the form's slaves before inserting. | | `/filterTree/*` | `FilterTreeController` | Tree-shaped filter dropdown for grids. | | `/notClear/*` | `NotClearController` | Barcode-scan "not yet cleared" save path (`doNotClearSave`, `getNotClearScanData/{sProcName}/{sId}`). Specific to scanner-driven warehouse flows. | | `/notice/*` | `NoticeController` | In-app notice fetch / mark-read. | | `/replaceField/*` | `ReplaceFieldController` | Bulk field-replace across rows. | | `/searchgroupby/*` | `SearchgroupbyController` | Saved-search definitions with group-by. | | `/searchupdown/*` | `SearchUpDownController` | Previous-/next-record-by-search navigation (variant of `/business/getBusinessDataByIndex`). | | `/syssearch/*` | `SyssearchController` | Saved-search definitions CRUD. | | `/syssystem/*` | `SyssystemController` | Variant of `getBusinessDataByFormcustomId` for system-table reads (`/getSyssystemDataByFormcustomId/{gdsconfigformmasterId}`) — bypasses tenant scoping for global metadata reads. | | `/sqlfile/*` | `SqlFileController` | SQL-file load/save backing the "Mysql脚本配置" admin screen. | | `/instruct/*` | `InstructController` | Direct SQL execution endpoints (`/exesql`, `/opensql`) — the admin-side query console. | ### User and permission management (4) Multiple overlapping controllers for the same concern. The `New` suffix and the presence of `sftlogininfo` *and* `userinfo` *and* `gdslogininfo` (in `systemweb/`) suggest a refactor in progress — the older path coexists with the new one until callers migrate. | Endpoint root | Controller | Role | |---|---|---| | `/userinfo/*` | `UserInfoController` | Current-user profile + session info. | | `/sftlogininfo/*` | `SftlogininfoController` | User-account CRUD (newer of two paths). | | `/sysjurisdiction/*` | `SysjurisdictionController` | Group/user permission reads (`getGroupData`, `getUserData`). | | `/sysjurisdictionNew/*` | `SysjurisdictionNewController` | Newer parallel path (`getGroupDataNew`, `getGroupUserIdNew/{sUserId}`). Same concern, different shape. | ### Manufacturing / MES (7) Industry-tier flows whose state machines, multi-table joins, or hardware integration could not be expressed as `gdsconfigformmaster` SQL. These are the framework's largest single concentration of hardcoded business logic. | Endpoint root | Controller | Role | |---|---|---| | `/sysworkorder/*` | `WorkOrderController` | Work-order CRUD with side effects (`/add`, `/update/{sId}`) — a printing-industry work order spans many slaves and proc invocations the universal save can't chain atomically. | | `/workOrderFlow/*` | `WorkOrderFlowController` | Work-order routing / process-flow reads (`getWorkOrderFlow`, `getSplitWorkOrderData/{sId}`). | | `/workOrderPlan/*` | `WorkOrderPlanController` | Production-plan reads linking work orders to plan rows (`getControlProcess/{sProductionPlanId}`, `getProductionPlanInfo`). | | `/splitWorkOrder/*` | `SplitWorkOrderController` | Splitting a master work order into multiple sub-orders (`getSplitWorkOrderData`). | | `/productionPlan/*` | `ProductionPlanController` | Plan-tree reads (`getProductionPlanTree`). | | `/process/*` | `ProcessController` | Manufacturing-process catalog reads. | | `/oee/*` | `OeeController` | Overall Equipment Effectiveness — barcode-scan and MES status callbacks (`updateBarcode/{sBarCodeId}/{sBarCode}`, `doSysMesMsg/{sStatus}/{sMachineId}`). | ### Sales, inventory, accounting, procurement, HR (9) | Endpoint root | Controller | Role | |---|---|---| | `/salesorder/*` | `SalesOrderController` | Sales-order specifics beyond the universal CRUD. | | `/business/addQuotationsheet`, `/business/getQuoquotationProgress` | `QuoquotationController` | Quotation-sheet creation (long-running, hence the progress endpoint). **Note:** shares the `/business/*` prefix with `BusinessBaseController` — see the namespace-overlap note above. | | `/eleMaterialsStock/*` | `EleMaterialsStockController` | Raw-material stock reads (`getEleMaterialsStock`, `getEleMaterialsStoreCurrQty`). | | `/eleProductStock/*` | `EleProductStockController` | Finished-product stock reads. | | `/costCenter/*` | `CostCenterController` | Cost-center data + voucher-import (`getCostCenterData`, `getCosvoucherImportData`). | | `/sysAccountPeriod/*` | `SysAccountPeriodController` | Accounting-period open/close logic. | | `/erpOrderProcurement/*` | `ErpOrderProcurementController` | Procurement-order specifics. | | `/sisproductclassify/*` | `SisproductclassifyController` | Product-classification tree. | | `/eleteamemployee/*` | `EleteamemployeeController` | Team/employee assignment for shop-floor flows. | ### Integration and hardware (5) | Endpoint root | Controller | Role | |---|---|---| | `/file/*` | `FileController` | File upload (incl. WeChat mobile variant `mobileuploadwechat`). | | `/plc/*` | `PlcController` | PLC-bridge entry points (`getplcMachine/{iOrder}/{sParentId}`) — see [Slice 6](../slices/06-hardware.md). | | `/mobilephone/*` | `MobliePhoneController` | Mobile-app endpoints. (Typo `Moblie` is in the class name; the URL is `/mobilephone`.) | | `/sysWebsocket/*` | `SysWebSocketController` | WebSocket setup/teardown for push notifications. | | `/wechat/*` | `WechatController` | WeChat integration (in-app QR, OAuth callback). | ### Out-of-scope per [the index](../index.md) (5) Listed for completeness — these are not part of the framework wiki's in-scope surface, but they exist in the WAR. | Endpoint root | Controller | Status | |---|---|---| | `/ai/*` | `AiController` | AI assistant. Out of scope (index.md). | | `/robot/*` | `ChatGptController` | ChatGPT integration. Out of scope (index.md). | | `/test/*` | `TestController` | Dev scaffolding (`/file`, `/getDinkToken`). | | (root paths) | `TestProcessController` | Dev scaffolding; no class-level `@RequestMapping`. | | (commented out) | `XsController` | Dead file — `@RestController` and `@RequestMapping` are commented out; class exists but registers nothing. | ### Reading these as a diagnostic Three patterns stand out when you scan the list: 1. **Long-running or multi-step transactions** (`QuoquotationController.getQuoquotationProgress`, `BillController.billCopyToCheck`, `SplitWorkOrderController`) — the universal save is single-shot; any flow that needs a progress endpoint or a "clone then redirect" semantic needs its own controller. 2. **Industry-specific state machines** (`OeeController`, work-order family, `SysAccountPeriodController`) — when "the next legal state" can't be derived from a single column, the proc-dispatch path isn't enough and the controller wires up the orchestration in Java. 3. **Hardware or external systems** (`PlcController`, `WechatController`, `SysWebSocketController`, `FileController`) — anything that isn't "MySQL + HTTP" needs Java glue; metadata can't describe an inbound websocket or a serial-port handshake. The framework's universal runtime is what's *missing* from these controllers' jobs. ## What this API is *not* - **Not stable** — endpoint shapes change with the framework. - **Not authenticated for outside callers** — there is no API-key flow here; cookies/sessions are not what an integrator wants. - **Not documented for self-service** — the surface is too large and too generic to publish as an OpenAPI doc. External integrators get the curated [External API](external.md) instead.