# 内部 API（`xlyEntry`）

`xlyEntry` 服务承载 SPA 的运行时 API。它是三层中最大的一层：控制器和框架的元数据驱动运行时编译到同一个 WAR，大多数调用会落到少数几个可以读写任意模块的通用端点上。

这个 API **不是给外部调用方使用的稳定契约**。端点形状会随框架变化而变化。外部集成应该使用[外部 API](external.md)。本页面面向维护人员和 SPA 扩展作者。

请求生命周期和代码级 walkthrough 见[维护人员运行时章节](../reference/maintainer/runtime.md)。本页只列 HTTP 入口。

## 通用 CRUD 接口面：`/business/*`

| 端点 | 方法 | 用途 |
|---|---|---|
| `/business/getModelBysId/{sModelsId}` | GET | 返回某个模块的表单布局，也就是五键组合（`formData`、`gdsformconst`、`gdsjurisdiction`、`billnosetting`、`report`）。 |
| `/business/getBusinessDataByFormcustomId/{gdsconfigformmasterId}` | POST | 返回某个表单的业务数据行，带分页。设置 `sGroupList` 时会分支到 `getBusinessDataByGroup`。 |
| `/business/getBusinessDataByIndex` | POST | 首条 / 末条 / 下一条 / 上一条记录导航。 |
| `/business/addBusinessData` | POST | 单行新增。 |
| `/business/addUpdateDelBusinessData` | POST | 在一个事务调用里组合新增 + 修改 + 删除。前端通过 `sTable` 直接指定目标表。 |
| `/business/getSelectDataBysControlId/{sId}` | POST | 按控件 `sId` 为单个控件加载下拉选项。 |
| `/business/getSelectLimit/{sId}` | POST | 下拉加载调用的分页变体。 |
| `/business/addSysLocking` | POST | 用户开始编辑单据时获取乐观锁，在系统锁表中按 `(sFormGuid, sUserId)` 插入一行。SPA 进入编辑模式时会触发它，使并发编辑者收到冲突提示。处理器：`BusinessBaseController.java:400-407`。 |
| `/business/doExamine` | POST | 简单“审核”：通过 SQL 把指定行的 `bCheck` 翻为 `1`。**不会调用 Activiti**；这是不需要多步工作流的模块使用的 xly 轻量审批路径。处理器：`BusinessBaseController.java:384-391` → `BusinessBaseServiceImpl.doExamine` → `ExamineServiceImpl`。何时改用 Activiti，见 [xly 如何在不使用 Activiti 的情况下处理工作流](../reference/maintainer/activiti.md#xly-如何在不使用-activiti-的情况下处理工作流)。 |
| `/business/getProData` | POST | 面向模块的通用存储过程调用，是 `/procedureCall/doGenericProcedureCall` 的另一条路径。处理器：`BusinessBaseController.java:350-358` → `BusinessBaseServiceImpl.getProData`。FROUNT 会用它做模块级 proc 读取，例如首页看板的 `/getProData?sModelsId=...&sName=` 模式。 |

这些端点在[切片 1](../slices/01-hello-world.md)（`getModelBysId` + 网格加载 + 保存）和[切片 3](../slices/03-report.md)（基于视图的读取变体）中有更详细说明。处理类位于 `xlyEntry/src/main/java/com/xly/web/businessweb/`。

## 元数据管理端点

配置侧动作（创建模块、定义表单、声明虚拟表）在 `xlyEntry/src/main/java/com/xly/web/systemweb/` 下有一套并行接口面：

| 端点前缀 | Controller | 用途 |
|---|---|---|
| `/gdsmodule/*` | `GdsmoduleController` | 模块树 CRUD，包括 `getModuleTreePro`、`addGdsmodule`、`updateGdsmodule`。 |
| `/gdsconfigform/*` | `GdsconfigformController` | 表单主表和表单明细元数据 CRUD。 |
| `/gdsconfigtb/*` | `GdsconfigtbController` | 虚拟表主表 / 明细元数据 CRUD。 |

## 专用运行时端点

| 端点前缀 | Controller | 用途 |
|---|---|---|
| `/configform/*` | `BusinessConfigformController` | 用户 / 用户组级显示定制。 |
| `/treegrid/*` | `BusinessTreeGridController` | 树表端点（当前分支实现的是存储过程支撑路径）。 |
| `/procedureCall/*` | `GenericProcedureCallController` | 按名称 + 参数通用调用存储过程；见[通用存储过程分发](../reference/maintainer/proc-dispatch.md)。 |
| `/panel/*` | `ConfigformPanelController` | `gdsconfigformpanel` 中的面板布局持久化。 |
| `/checkflow/*` | `CheckFlowController` | **空壳，返回 404。** 该类只声明前缀，没有任何 handler 方法。真正的工作流审核 / 驳回 / 完成 URL 来自 xlyFlow 的 `CurrencyFlowController`（因为 xlyFlow 作为库依赖被编进 xlyEntry，所以仍挂在 xlyEntry context-path 下）：`/currencyFlow/complete/{taskId}/{sBrandsId}/{sSubsidiaryId}/{sUserId}`、`/currencyFlow/completeerp/{sBrandsId}/{sSubsidiaryId}/{sUserName}`，以及根路径映射的 BPMN modeler `/modeler/*`。见 [Activiti 集成](../reference/maintainer/activiti.md)。 |
| `/modelCenter/getModelCenter`、`/modelCenter/getModelCenterCalculation` | `BusinessModelCenterController` | FROUNT 首页的 **KPI 工作中心**卡片（标题为 `KPI监控`）。它聚合标记为 `gdsmodule.bUnTask=1` 的模块上的未清任务，按角色和业务流程分组。**不是 Activiti 驱动。** 见 [KPI 工作中心](../reference/maintainer/runtime.md#kpi-工作中心front-端首页-dashboard)。 |

## 报表与打印

打印接口面位于 `xlyEntry/src/main/java/com/xly/web/report/`：

- `PrintReportController` — 当前 jxls / iText 打印路径。
- `PrintReportControllerOld.java` — 文件存在，但类体已全部注释掉（而且注释内类名是 `PrintReportController`，不是 `*Old`）。这是保留作参考的死代码，不是活跃 controller。

前端的“打印” / “导出”按钮会调用这些控制器，控制器从 `sysreport` 加载模板，执行匹配的视图查询，并把二进制文件流回前端。流程见[切片 3](../slices/03-report.md)。

## 认证

所有参与业务数据的控制器方法都标注 `@Authorization`，并通过 `@CurrentUser` 接收解析后的 `UserInfo`。session 到 `UserInfo` 的映射由框架自己处理（cookie + Redis 支撑的 session）；见[多租户概念页](../concepts/multi-tenancy.md)。

如果某个请求未认证却进入了控制器，正常情况下会被 `@Authorization` 拦下；如果没有被拦下（例如某个方法未加注解），这个方法也会绕过 `RequestAddParamUtil` 中的通用租户注入，因此就是多租户 bug。

## BACK 配置侧边栏（管理接口面） {#back-builder-sidebar-admin-surface}

BACK 管理侧边栏的 10 个顶层项（登录 `admin`/`123`，版本 `基础版/8s`）都通过上面的框架原语接线成元数据驱动页面：

| 侧边栏 | URL 片段 | 支撑 form-master `sTbName` | 所属 service |
|---|---|---|---|
| 系统模块配置 | `/xtmkpz` | `gdsmodule` | `GdsmoduleServiceImpl` |
| 数据表内容配置 | n/a | `gdsconfigtbmaster`/`slave` | `GdsconfigtbServiceImpl` |
| 界面显示内容配置 | n/a | `gdsconfigformmaster`/`slave`/`customslave`/`personalize` | `GdsconfigformServiceImpl` |
| 接口自定义配置 | `/sjbnrpz` | `sysapi` 表族 | xlyApi 侧管理接口 |
| 系统常量配置 | `/xtclpz` | `gdsformconst` | `GdsformconstServiceImpl`，切片 1 锚点 |
| 系统权限配置 | n/a | `gdsjurisdiction` | `GdsjurisdictionServiceImpl` |
| 常用操作配置 | n/a | 当前 dev DB 中没有对应 `gdsconfigformmaster` 行；页面是 SPA 中直接接线的管理特例。若通过元数据扩展，数据位于用户自定义按钮组层。 | n/a |
| 用户信息配置 | n/a | `sftlogininfo` 表族 | `GdslogininfoServiceImpl` |
| Mysql脚本配置 | n/a | BACK 对 [`templesql/` 脚手架](../reference/maintainer/sql-templates.md)的编辑器 | `SqlScriptsServiceImpl` |
| 图表配置 | 无 `gdsroute` 项；通过 SPA state 导航 | `gdsconfigcharmaster`/`slave` | `GdsconfigformServiceImpl`（图表子集） |

10 项里有 8 项是本目录和[维护人员参考](../reference/maintainer/management-services.md)覆盖的框架原语。**`常用操作配置` 是 SPA 侧管理特例**：它出现在侧边栏，但 dev DB 中没有对应的 `gdsconfigformmaster` 行，说明该页在 BACK 中硬编码，而不是元数据驱动。**`图表配置`** 则完全由元数据驱动：两条 `gdsconfigformmaster` 行分别指向 `gdsconfigcharmaster` 和 `gdsconfigcharslave`，其中的图表定义由 SPA 其他位置的看板渲染消费。

## 框架原语之外：xlyEntry 的其余接口面

`xlyEntry` 总共托管 **70 个 controller**。其中 18 个属于框架侧：上面明确列出的通用运行时 controller，加上支撑 [BACK 配置侧边栏](#back-builder-sidebar-admin-surface) 的 7 个 `systemweb/` 管理 controller（`GdsformconstController`、`GdsjurisdictionController`、`GdslogininfoController`、`GdsparameterController`、`LicenseController`、`LoginController`、`SysbrandsController`）。每个元数据驱动表单的生命周期都在这些接口面里。

剩余 52 个 controller 存在，是因为框架的通用 CRUD + 存储过程分发路径**不足以表达对应用例**。换句话说，每一个都是数据驱动论点停止扩展的位置标记。

即使它们不属于框架目录化接口面，也值得枚举：它们展示了 xly 哪些内容硬编码在 Java 里，哪些内容留给元数据。维护人员读这些 controller，能直接看到框架逃生口的形状。

> **命名空间重叠。** `BusinessBaseController`（`/business/*`）和 `QuoquotationController`（同样 `@RequestMapping("/business")`）共享 URL 前缀。Spring 会按方法级路径解析，所以 `/business/addQuotationsheet` 和 `/business/getQuoquotationProgress` 落在 `QuoquotationController`，其他 `/business/*` 端点落在 `BusinessBaseController`。当前没有方法路径冲突，因此能正常工作；但这个约定很容易踩坑：未来如果有人在 `BusinessBaseController` 里新增 `@PostMapping("/addQuotationsheet")`，就会悄悄遮蔽报价路径。

### 表单辅助与 SPA 扩展 controller（22）

这些是贴近框架的端点，是通用 CRUD 路径的扩展，但不适合放进 form-master / form-slave 的固定形状里。多数会在同一个 SPA 页面里和 `/business/*` 一起被调用。

| 端点前缀 | Controller | 角色 |
|---|---|---|
| `/bill/*` | `BillController` | 复制单据操作（`billCopyToCheck`、`billCopyToCheckWork`）：把一组 master + slave 行克隆成新单据。它不放在 `/business/*` 下，是因为 SPA 需要在一次请求里拿到新 `sId` 集合，而通用保存端点没有这种响应形状。 |
| `/change/*` | `ChangeController` | 通用字段变更重算（`changeParam`）：字段值触发派生重算且需要调用存储过程时，SPA 会调用它。 |
| `/parameter/*` | `BusinessParameterController` | 每模块参数读取。 |
| `/treeclassify/*` | `BusinessTreeClassifyController` | 表单数据加载的树分组变体：`/treeclassify/getTreeClassify/{gdsconfigformmasterId}`。它位于 `/business/*` 之外，是因为响应形状是嵌套树，不是平铺行集。 |
| `/calcprocedure/*` | `CalcProcedureController` | [计算公式](../reference/builder/define-vtable.md) 功能的运行时侧：`/calc` 调用命名计算过程。 |
| `/calculationFormula/*` | `CalculationFormulaController` | 计算公式的构建侧元数据。 |
| `/calculationStd/*` | `CalculationStdController` | 标准计算查找目录。 |
| `/char/*` | `CharController` | BACK 图表配置页面的图表配置 CRUD，包装 `gdsconfigcharmaster` / `slave`。 |
| `/checkModel/*` | `CheckmodelController` | 审批模型成员读取（`getUserListByModelId/{sCheckModeId}`），供轻量级（非 Activiti）审批流使用。 |
| `/comparatorTree/*` | `ComparatorTreeController` | 可过滤层级选择器使用的比较树读取。 |
| `/excel/*` | `ExcelController` | 网格 **Excel 导出**：`/export/{gdsconfigformmasterId}`。它是打印的兄弟路径，但输出数据而不是报表版式。 |
| `/import/*` | `ImportExcelController` | Excel **导入**：先 `/checkExcel` 校验，再提交。插入前会按表单 slave 定义验证。 |
| `/filterTree/*` | `FilterTreeController` | 网格过滤用树形下拉。 |
| `/notClear/*` | `NotClearController` | 条码扫描“未清”保存路径（`doNotClearSave`、`getNotClearScanData/{sProcName}/{sId}`），特定于扫描驱动的仓库流程。 |
| `/notice/*` | `NoticeController` | 站内通知获取 / 标记已读。 |
| `/replaceField/*` | `ReplaceFieldController` | 跨行批量字段替换。 |
| `/searchgroupby/*` | `SearchgroupbyController` | 带 group-by 的保存搜索定义。 |
| `/searchupdown/*` | `SearchUpDownController` | 按搜索结果上一条 / 下一条导航，是 `/business/getBusinessDataByIndex` 的变体。 |
| `/syssearch/*` | `SyssearchController` | 保存搜索定义 CRUD。 |
| `/syssystem/*` | `SyssystemController` | 系统表读取专用的 `getBusinessDataByFormcustomId` 变体（`/getSyssystemDataByFormcustomId/{gdsconfigformmasterId}`），用于全局元数据读取时绕过租户作用域。 |
| `/sqlfile/*` | `SqlFileController` | “Mysql脚本配置”管理页面背后的 SQL 文件加载 / 保存。 |
| `/instruct/*` | `InstructController` | 直接执行 SQL 的端点（`/exesql`、`/opensql`）：管理侧查询控制台。 |

### 用户与权限管理（4）

同一关注点下有多个重叠 controller。`New` 后缀，以及 `sftlogininfo`、`userinfo`、`gdslogininfo`（在 `systemweb/` 中）同时存在，说明这里有一次迁移中的重构：旧路径和新路径会并存，直到调用方迁移完成。

| 端点前缀 | Controller | 角色 |
|---|---|---|
| `/userinfo/*` | `UserInfoController` | 当前用户资料与 session 信息。 |
| `/sftlogininfo/*` | `SftlogininfoController` | 用户账号 CRUD（两条路径中较新的一个）。 |
| `/sysjurisdiction/*` | `SysjurisdictionController` | 用户组 / 用户权限读取（`getGroupData`、`getUserData`）。 |
| `/sysjurisdictionNew/*` | `SysjurisdictionNewController` | 较新的并行路径（`getGroupDataNew`、`getGroupUserIdNew/{sUserId}`）。关注点相同，形状不同。 |

### 生产 / MES（7）

这些是行业层流程；它们的状态机、多表连接或硬件集成无法只靠 `gdsconfigformmaster` SQL 表达。这是框架中硬编码业务逻辑最集中的一组。

| 端点前缀 | Controller | 角色 |
|---|---|---|
| `/sysworkorder/*` | `WorkOrderController` | 带副作用的工单 CRUD（`/add`、`/update/{sId}`）：印刷行业工单会跨很多 slave 和存储过程调用，通用保存端点无法原子串起这些操作。 |
| `/workOrderFlow/*` | `WorkOrderFlowController` | 工单工艺路线 / 流程读取（`getWorkOrderFlow`、`getSplitWorkOrderData/{sId}`）。 |
| `/workOrderPlan/*` | `WorkOrderPlanController` | 连接工单与计划行的生产计划读取（`getControlProcess/{sProductionPlanId}`、`getProductionPlanInfo`）。 |
| `/splitWorkOrder/*` | `SplitWorkOrderController` | 把一个主工单拆成多个子工单（`getSplitWorkOrderData`）。 |
| `/productionPlan/*` | `ProductionPlanController` | 计划树读取（`getProductionPlanTree`）。 |
| `/process/*` | `ProcessController` | 生产工序目录读取。 |
| `/oee/*` | `OeeController` | OEE（Overall Equipment Effectiveness，设备综合效率）：条码扫描和 MES 状态回调（`updateBarcode/{sBarCodeId}/{sBarCode}`、`doSysMesMsg/{sStatus}/{sMachineId}`）。 |

### 销售、库存、财务、采购、人事（9）

| 端点前缀 | Controller | 角色 |
|---|---|---|
| `/salesorder/*` | `SalesOrderController` | 超出通用 CRUD 的销售订单专用逻辑。 |
| `/business/addQuotationsheet`、`/business/getQuoquotationProgress` | `QuoquotationController` | 报价单创建（长运行，因此有进度端点）。**注意：**它和 `BusinessBaseController` 共享 `/business/*` 前缀，见上面的命名空间重叠说明。 |
| `/eleMaterialsStock/*` | `EleMaterialsStockController` | 原材料库存读取（`getEleMaterialsStock`、`getEleMaterialsStoreCurrQty`）。 |
| `/eleProductStock/*` | `EleProductStockController` | 成品库存读取。 |
| `/costCenter/*` | `CostCenterController` | 成本中心数据与凭证导入（`getCostCenterData`、`getCosvoucherImportData`）。 |
| `/sysAccountPeriod/*` | `SysAccountPeriodController` | 会计期间启用 / 关闭逻辑。 |
| `/erpOrderProcurement/*` | `ErpOrderProcurementController` | 采购订单专用逻辑。 |
| `/sisproductclassify/*` | `SisproductclassifyController` | 产品分类树。 |
| `/eleteamemployee/*` | `EleteamemployeeController` | 车间流程中的班组 / 员工分配。 |

### 集成与硬件（5）

| 端点前缀 | Controller | 角色 |
|---|---|---|
| `/file/*` | `FileController` | 文件上传，包括微信移动端变体 `mobileuploadwechat`。 |
| `/plc/*` | `PlcController` | PLC 桥接入口（`getplcMachine/{iOrder}/{sParentId}`），见[切片 6](../slices/06-hardware.md)。 |
| `/mobilephone/*` | `MobliePhoneController` | 移动 App 端点。（类名中的 `Moblie` 拼写错误来自源码；URL 是 `/mobilephone`。） |
| `/sysWebsocket/*` | `SysWebSocketController` | 推送通知的 WebSocket 建立 / 关闭。 |
| `/wechat/*` | `WechatController` | 微信集成（站内二维码、OAuth 回调）。 |

### 按[首页范围说明](../index.md)排除（5）

为完整性列出；它们不属于本框架 Wiki 的范围内接口面，但确实存在于 WAR 中。

| 端点前缀 | Controller | 状态 |
|---|---|---|
| `/ai/*` | `AiController` | AI assistant。范围外（见 index.md）。 |
| `/robot/*` | `ChatGptController` | ChatGPT 集成。范围外（见 index.md）。 |
| `/test/*` | `TestController` | 开发脚手架（`/file`、`/getDinkToken`）。 |
| （根路径） | `TestProcessController` | 开发脚手架；没有类级 `@RequestMapping`。 |
| （已注释） | `XsController` | 死文件：`@RestController` 和 `@RequestMapping` 都被注释掉；类存在，但不会注册任何端点。 |

### 把这些 controller 当作诊断线索

扫完整个列表后，三个模式很明显：

1. **长运行或多步骤事务**（`QuoquotationController.getQuoquotationProgress`、`BillController.billCopyToCheck`、`SplitWorkOrderController`）：通用保存是一次性请求；任何需要进度端点，或“克隆后跳转”语义的流程，都需要自己的 controller。
2. **行业专用状态机**（`OeeController`、工单家族、`SysAccountPeriodController`）：当“下一个合法状态”不能从单个列推出时，存储过程分发路径就不够用了，controller 会在 Java 中串起编排逻辑。
3. **硬件或外部系统**（`PlcController`、`WechatController`、`SysWebSocketController`、`FileController`）：凡是不只是“MySQL + HTTP”的能力，都需要 Java 胶水；元数据无法描述入站 websocket 或串口握手。

这些 controller 的职责，正好就是框架通用运行时没有覆盖的部分。

## 这个 API 不是什么

- **不是稳定接口。** 端点形状会随框架变化。
- **不是给外部调用方认证的接口。** 这里没有 API key 流程；cookie/session 不是集成方需要的东西。
- **不是自助式公开文档。** 这个接口面太大、太通用了，不适合发布成 OpenAPI 文档。外部集成方应该使用经过收敛的[外部 API](external.md)。
