# 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.
