# 切片 3 — 由视图支撑的模块(只读报表) 切片 1 追踪的是 CRUD 模块:表单读写一张表。切片 3 前进一步:模块**从数据库视图读取**,连接多张基础表,并把连接结果呈现为可排序、可过滤的表格。没有保存,没有删除,只读。 这是 xly 中所有“列表视图”的基础:订单汇总、日报、库存快照、KPI 看板。框架通过与表支撑模块*相同*的元数据管线处理视图支撑模块,唯一差异是 `gdsconfigformmaster.sType = 'view'` 会抑制写操作。 ## 记录对象 | | | |---|---| | **模块** | `工单工序明细`(Work-Order Process Details) | | **URL 片段** | `/indexPage/commonList`(通用“只读列表”页面模板) | | **模块 `sId`** | `19211681019715708462888040` | | **支撑对象** | [`viw_mftworkorderprocess`](../auto-catalog/views/viw_mftworkorderprocess.md),数据库视图,**不是**基础表 | | **字段数** | 40 | | **写过程** | 无,设计上只读 | | **打印模板** | 无,纯表格 | 该视图把工单 master / slave 表族连接成扁平的每行表示。视图本身的存在就是 xly 报表渲染约定的一部分:与其在每个读取查询中重新写 join,xly 会编写数据库侧 `CREATE VIEW`,并让一行元数据指向它。 ## 为什么选这个模块 {#why-this-module} 它是 xly 最常见运行模式之一的最简单成员:**视图支撑的列表页**。这个 dev DB 中有 405 个视图支撑表单(全部表单的 20%),仅次于表支撑表单。理解本切片后,其他运维报表都是变体。 它也反驳切片 1 可能造成的两个误解: 1. 并非每个模块都从单表读取;视图支撑模块读取 join。 2. 并非每个 URL 都是唯一拼音路径。`/indexPage/commonList` 是**共享模板 URL**,许多不同模块都使用它。URL 不标识模块;API 调用上的 `sModelsId` 参数才标识模块。 ## 追踪 ### 1. 页面 shell 与切片 1 相同:访问 `/indexPage/commonList` 进入 SPA shell;SPA 再通过侧边栏 / 菜单状态解析要加载的模块。URL 是*显示状态*,不是路由驱动。 ### 2. 元数据获取 ```text GET /xlyEntry/business/getModelBysId/19211681019715708462888040?sModelsId=19211681019715708462888040 ``` 同一个 handler,同一个五键复合响应:`formData`、`gdsformconst`、`gdsjurisdiction`、`billnosetting`、`report`。这里的 `report` 为空,因为该模块没有附加打印模板。 与切片 1 的关键差异在 `formData`:form-master 行包含: ```text sType = 'view' sTbName = 'viw_mftworkorderprocess' sSqlStr = SELECT ... FROM viw_mftworkorderprocess ... 骨架 sWhere/sOrder = 默认谓词 ``` `sType = 'view'` 是框架判断只读的唯一信号。其余元数据结构与 CRUD 模块相同。 ### 3. 表格数据获取 ```text POST /xlyEntry/business/getBusinessDataByFormcustomId/{formId}?sModelsId={moduleId} ``` 仍然是切片 1 的同一端点。handler 不关心支撑对象是表还是视图,只把 `sTbName` 代入由 `sSqlStr` + `sWhere` + `sOrder` 组合出的参数化 SQL。视图负责 join;框架负责租户过滤和分页。 > **为什么用视图而不是运行时 JOIN?** xly 把 join 写进 SQL DDL(`CREATE VIEW`),使运行时 SQL 保持扁平的 `SELECT ... FROM WHERE ...`。这样可以把 table、view、proc 等不同“形状”来源插入同一表单机制。代价是命名视图增多;此 dev DB 中有 311 个。 ### 4. 视图本身 `viw_mftworkorderprocess` 把工单 master 与每工序 slave 明细连接起来。完整 `CREATE VIEW` 定义见[该视图的自动目录页](../auto-catalog/views/viw_mftworkorderprocess.md)。 命名约定:xly 视图前缀是 `viw_` 或 `Viw_`(schema 中大小写不一致)。join 视图常常很宽,40+ 列很常见。它们几乎总是从主基础表带出 `sBrandsId` / `sSubsidiaryId`,使框架的通用租户过滤仍然有效。 ### 5. 保存:不存在 对 `sType = 'view'`,框架不提供新增 / 更新 / 删除按钮。理论上,如果被要求,`addUpdateDelBusinessData` 的 MyBatis 侧会尝试写入视图;但框架不会为它渲染按钮。需要让某个视图可写时,维护人员通常应把表单指向基础表并使用自定义保存过程,或依赖 MySQL 自动可更新视图能力;本模块都没有这样做。 ### 6. 打印报表(存在时) 一些视图支撑模块确实有打印模板:Excel via jxls、PDF via iText。机制与表格分离: - `getModelBysId` 返回 `report` 数组,来自通过 `sFormId` 关联到表单的 `sysreport` 行。 - 前端“打印” / “导出”按钮调用 `xlyEntry/com/xly/report/` 下的 controller,加载 jxls / iText 模板,使用同一视图查询的“取全部行”包装,并把二进制文件流回。 - 本模块没有模板,因此不覆盖打印路径。未来修订应选一个确实有模板的模块;`print template` 值得单独成章。 ## 本切片引入或强化的概念 - *视图支撑模块*:`gdsconfigformmaster.sType = 'view'` 让模块只读,其余元数据流相同。 - *共享模板 URL*:`/indexPage/commonList`、`/indexPage/commonBill`、`/indexPage/commonClassify`、`/indexPage/commonNewBill` 被数百个模块复用。URL 选择页面形状;模块身份来自 `sModelsId`。 - *报表模板*(仅预览):`sysreport` 通过 `sFormId` 关联,jxls / iText 模板由 `PrintReportController` 提供。 ## 待验证项 1. **带打印模板的视图支撑模块**:选择一个并端到端追踪 jxls 导出。 2. **`sType = 'proc'` 变体**:209 个表单由存储过程支撑,应另开切片说明过程如何返回结果集和参数如何流动。 3. **视图租户安全。** 需要脚本审计哪些视图没有带出 `sBrandsId` / `sSubsidiaryId`。