From 7c1f135e99e59cd919f76653d5f2ac06a5d160ed Mon Sep 17 00:00:00 2001 From: caozehui <2427765068@qq.com> Date: Fri, 12 Jun 2026 09:49:30 +0800 Subject: [PATCH] docs: add report template pattern design --- ...26-06-12-report-template-pattern-design.md | 451 ++++++++++++++++++ 1 file changed, 451 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-12-report-template-pattern-design.md diff --git a/docs/superpowers/specs/2026-06-12-report-template-pattern-design.md b/docs/superpowers/specs/2026-06-12-report-template-pattern-design.md new file mode 100644 index 0000000..87040e9 --- /dev/null +++ b/docs/superpowers/specs/2026-06-12-report-template-pattern-design.md @@ -0,0 +1,451 @@ +# 报告模板 `pattern` 字段改造设计 + +## 背景 + +当前报告模板管理使用 `pq_report` 表,前端页面位于: + +- `frontend/src/views/system/template` +- `frontend/src/views/plan/planList/components/planPopup.vue` + +后端代码位于: + +- `D:\njcn\test\CN_Gather\detection\src\main\java\com\njcn\gather\report` +- `D:\njcn\test\CN_Gather\detection\src\main\java\com\njcn\gather\plan` + +数据库结构已新增字段: + +- `pattern char(32) COLLATE utf8mb4_bin NOT NULL COMMENT '模式id'` + +当前系统中的设备、计划、脚本、检测源等业务对象都已按模式隔离,但报告模板仍是全局管理: + +- 模板列表未按模式过滤 +- 模板新增/编辑没有模式归属 +- 计划弹窗的报告模板下拉取的是全部模板 +- 模板文件目录与模式无关 + +本次改造的目标是让报告模板纳入现有的模式上下文管理,但保持“模式为隐式上下文”,不增加用户额外操作负担。 + +## 目标 + +- 报告模板列表按当前模式过滤 +- 报告模板新增/编辑时自动绑定当前模式 `pattern` +- 报告模板前端页面不展示模式列,不展示模式下拉 +- 计划弹窗中的报告模板选项只返回当前模式模板 +- 删除不再使用的 `getPqReportAllName()` 前后端接口和调用 +- 模板落盘目录改为 `patternName/name/version/` +- 模板重复校验改为 `pattern + name + version` + +## 非目标 + +- 不增加模式列展示 +- 不在新增/编辑弹窗中增加模式下拉 +- 不在计划绑定模板时校验 `plan.pattern == report.pattern` +- 不在生成报告时校验 `plan.pattern == report.pattern` +- 不处理旧模板数据补 `pattern`,由数据库手工修复 +- 不调整 `ad_plan.reportTemplateId` 的现有绑定模型 + +## 业务约束 + +### 模式来源 + +前端不允许用户手工选择模板模式。页面中的模板模式始终取“当前模式”上下文: + +- 来源:`modeStore.currentMode` +- 映射:`dictStore.getDictData('Pattern')` 中与当前模式名称匹配的数据字典项 +- 实际提交值:字典项 `id` + +### 目录规则 + +模板文件存储目录由当前的: + +- `name/version/` + +改为: + +- `patternName/name/version/` + +其中: + +- `patternName` 取模式字典的 `name` +- 数据库存储的 `pattern` 仍然是模式字典 `id` +- 目录名在落盘前需要做 Windows 路径安全处理 + +### 模板唯一性 + +模板唯一性由当前的全局: + +- `name + version` + +改为: + +- `pattern + name + version` + +因此: + +- 同一模式下不允许同名同版本模板 +- 不同模式下允许同名同版本模板 + +## 数据模型调整 + +### 后端实体与参数 + +以下对象补充 `pattern` 字段: + +- `PqReport` +- `ReportParam` +- `ReportParam.UpdateParam` +- `ReportParam.QueryParam` +- `PqReportVO` + +用途如下: + +- `PqReport.pattern`:数据库持久化 +- `ReportParam.pattern`:新增/编辑接收当前模式 id +- `ReportParam.QueryParam.pattern`:列表查询条件 +- `PqReportVO.pattern`:详情回显与后续兼容 + +### 数据库存储 + +- `pq_report.pattern` 保存模式字典 `id` +- 不额外保存 `patternName` +- `patternName` 仅在文件目录拼接时通过字典服务即时获取 + +## 后端接口设计 + +### 1. `POST /report/list` + +职责: + +- 分页查询当前模式下的报告模板 + +请求参数新增: + +- `pattern` + +处理规则: + +- 必须传 `pattern` +- 按 `pattern + name + version + state=1` 过滤 +- 不允许在未传 `pattern` 时返回全量模板 + +返回: + +- 保持现有分页结构 +- `PqReportVO` 中可包含 `pattern` +- 前端列表不展示该字段 + +### 2. `GET /report/getById` + +职责: + +- 查询模板详情 + +处理规则: + +- 保持现有按 `id` 查询 +- 返回结果包含 `pattern` + +说明: + +- 本次不额外增加 `id + pattern` 双重校验 +- 模板访问范围由列表入口和当前页面模式上下文收口 + +### 3. `POST /report/add` + +职责: + +- 新增报告模板 + +请求参数新增: + +- `pattern` + +处理规则: + +- `pattern` 必传 +- `pattern` 必须是合法模式字典 id +- 重复校验使用 `pattern + name + version` +- 文件目录按 `patternName/name/version/` 创建 + +### 4. `POST /report/update` + +职责: + +- 修改报告模板 + +请求参数新增: + +- `pattern` + +处理规则: + +- 前端仍会传当前模式 id +- 后端接收该字段 +- 不开放通过页面显式修改模式 +- 更新名称或版本时,模板目录迁移到新的 `patternName/name/version/` + +说明: + +- 本次不设计跨模式迁移能力 +- 模板模式的变更不作为用户可操作能力暴露 + +### 5. 新增模板选项接口 + +新增接口替代旧的全量模板名接口。 + +建议接口: + +- `GET /report/listOptions?pattern={patternId}` + +职责: + +- 返回当前模式下可选报告模板列表,供计划弹窗下拉使用 + +建议返回结构: + +- `id` +- `name` +- `version` +- `displayName` + +其中: + +- `displayName = name + '_' + version` + +说明: + +- 由后端拼接 `displayName` +- 前端不再自行拼接全量名称列表 + +### 6. 移除旧接口 + +以下接口与服务一并移除: + +- `GET /report/listAllName` +- `IPqReportService.listAllName()` +- `PqReportServiceImpl.listAllName()` +- 前端 `getPqReportAllName()` + +## 后端服务设计 + +### 列表查询 + +`PqReportServiceImpl.list()` 增加: + +- `pattern` 非空校验 +- `wrapper.eq("pattern", queryParam.getPattern())` + +### 重复校验 + +`checkRepeat()` 调整为: + +- `pattern` +- `name` +- `version` +- `state = ENABLE` + +### 模板目录构造 + +新增统一内部方法,例如: + +- `buildReportTemplateRelativeDir(String patternId, String name, String version)` + +职责: + +- 查询字典得到 `patternName` +- 对 `patternName`、`name`、`version` 做路径安全清洗 +- 生成相对目录 `patternName/name/version/` + +所有路径相关逻辑统一走该方法: + +- 新增模板 +- 更新模板 +- 重命名目录 +- 删除目录 + +### 文件目录迁移 + +编辑模板时: + +- 若 `name` 或 `version` 变化,按新目录规则迁移 +- 若未变化,则沿用原目录 +- 文件替换逻辑仍保持现有语义 + +### 模式字典读取 + +可直接复用现有字典服务: + +- `IDictDataService` + +用途: + +- 校验 `pattern` 是否存在 +- 读取 `pattern.name` + +## 前端页面设计 + +### 模板管理列表页 + +页面: + +- `frontend/src/views/system/template/index.vue` + +调整内容: + +- 查询列表时自动注入当前模式 `patternId` +- 列表不新增模式列 +- 列表不新增模式搜索项 +- 打开新增弹窗时,将当前模式 id 写入表单状态 + +### 模板新增/编辑弹窗 + +页面: + +- `frontend/src/views/system/template/components/reportPopup.vue` + +调整内容: + +- 本地表单对象增加 `pattern` +- 不展示模式下拉 +- 新增时自动写入当前模式 id +- 编辑时保留接口返回的 `pattern` 值,但不展示、不允许修改 +- 提交 `FormData` 时带上 `pattern` + +说明: + +- 模板模式是隐式上下文,不是表单可见字段 + +### 计划弹窗中的报告模板下拉 + +页面: + +- `frontend/src/views/plan/planList/components/planPopup.vue` + +调整内容: + +- 不再调用 `getPqReportAllName()` +- 改为调用新的按模式模板选项接口 +- 查询参数传当前模式 `patternId` +- 下拉只展示当前模式模板 +- 展示文案使用后端返回的 `displayName` + +说明: + +- 本次不调整计划保存的数据结构 +- 只调整模板下拉的取值范围和数据来源 + +## 前端 API 调整 + +文件: + +- `frontend/src/api/device/report/index.ts` +- `frontend/src/api/device/interface/report.ts` + +调整内容: + +- `PqReport.ReqReportParams` 增加 `pattern` +- `PqReport.ResReport` 增加 `pattern` +- `getPqReportList` 调用参数增加 `pattern` +- 删除 `getPqReportAllName` +- 新增按模式查询模板选项接口 + +## 异常处理 + +### 后端 + +- `/report/list` 未传 `pattern` 时,返回参数错误 +- `/report/add`、`/report/update` 中 `pattern` 为空时,返回参数错误 +- `pattern` 对应字典不存在时,返回参数错误 +- 路径安全清洗后若目录段为空,返回参数错误 +- 同模式下模板重名重版本时,返回重复模板错误 + +### 前端 + +- 当前模式找不到对应 `patternId` 时,不发起新增、编辑、列表查询 +- 提示当前模式配置异常 + +## 风险与处理 + +### 风险 1:旧目录与新目录并存 + +由于目录规则由 `name/version/` 改为 `patternName/name/version/`: + +- 未编辑过的旧模板文件可能仍在旧目录 +- 新增或编辑后的模板会进入新目录 + +处理: + +- 本次不做历史文件批量迁移 +- 以编辑后的新目录规则为准 + +### 风险 2:不同模式同名同版本模板的路径冲突 + +如果不把模式带入目录,会出现覆盖。 + +处理: + +- 目录规则强制引入 `patternName` +- 唯一性规则改为 `pattern + name + version` + +### 风险 3:计划绑定仍是弱约束 + +本次明确不校验: + +- `plan.pattern == report.pattern` + +因此若历史数据存在错绑: + +- 系统不会主动阻止 +- 但新的模板选择入口只暴露当前模式模板,后续错绑概率会明显下降 + +## 测试范围 + +### 后端验证 + +- 新增模板时成功写入 `pattern` +- 列表接口只返回当前模式模板 +- 同模式下不允许同名同版本 +- 不同模式下允许同名同版本 +- 模板目录为 `patternName/name/version/` +- 更新名称或版本时目录迁移正确 +- 新模板选项接口只返回当前模式模板 +- 旧 `listAllName` 接口与实现已彻底移除 + +### 前端联调 + +- 模板管理页在不同模式下看到不同模板列表 +- 新增模板时界面不展示模式字段,但提交体中带 `pattern` +- 编辑模板时界面不展示模式字段 +- 计划弹窗中报告模板下拉只显示当前模式模板 +- 前端已无 `getPqReportAllName()` 调用残留 + +## 实施范围 + +前端涉及: + +- `frontend/src/api/device/interface/report.ts` +- `frontend/src/api/device/report/index.ts` +- `frontend/src/views/system/template/index.vue` +- `frontend/src/views/system/template/components/reportPopup.vue` +- `frontend/src/views/plan/planList/components/planPopup.vue` + +后端涉及: + +- `com.njcn.gather.report.pojo.po.PqReport` +- `com.njcn.gather.report.pojo.param.ReportParam` +- `com.njcn.gather.report.pojo.vo.PqReportVO` +- `com.njcn.gather.report.controller.ReportController` +- `com.njcn.gather.report.service.IPqReportService` +- `com.njcn.gather.report.service.impl.PqReportServiceImpl` +- `com.njcn.gather.plan` 中报告模板下拉相关查询逻辑 + +## 结论 + +本方案将报告模板纳入已有模式体系,但保持“模式为页面隐式上下文”: + +- 用户不需要手工选择模式 +- 页面不展示模式字段 +- 模板列表、模板新增/编辑、计划模板下拉都以当前模式为边界 +- 目录结构与重复规则同时引入模式维度,避免数据和文件冲突 + +本方案不引入计划与模板的强一致校验,属于低侵入、可平滑上线的模式隔离改造。