# 内部 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)。