# hw-portal 官网模块说明(HwWeb / HwWeb1 / HwWebDocument / HwWebMenu / HwWebMenu1) > 仅关注与 `HwWeb`、`HwWeb1`、`HwWebDocument`、`HwWebMenu`、`HwWebMenu1` 相关的代码逻辑,方便 AI 与技术人员统一阅读和分析。 ## 0.后端代码注意事项 - hw-portal是微服务旧项目(springboot2+mybatis)的某个模块直接复制过来,现在是springboot3+MybatisPlus的单体新项目,需要将旧项目hw-portal模块的HwWeb、HwWeb1、HwWebDocument、HwWebMenu、HwWebMenu1的后端代码逻辑全部迁移到新项目的rfid-middleware\ruoyi-modules\hw-web模块中 - 注意只关注HwWeb、HwWeb1、HwWebDocument、HwWebMenu、HwWebMenu1! - 要求实体类的字段类型都与旧项目一致! - 注意前端的判断依据大多都是code! - 接口必须与旧项目完全一致,以方便前端直接调用! - 注意旧项目接口的权限控制已经全部注释掉了! - 要求代码逻辑不变,尽量贴合现在新项目的springboot3+MybatisPlus! - 代码的实现方式可以考虑MybatisPlus自带方法,比如说增删改查或者是逻辑删除,但是业务逻辑必须要一致! - 数据库表结构中is_delete字段0表示未删除,1表示已删除 - 注意现在新项目的文件服务也已经与旧项目完全不一样了,需要匹配现有新项目! - 数据库表结构与BaseEntity或者TenantEntity不一致,不应该继承,将相关字段填入在自己实体类中即可,注意@TableField(fill = FieldFill.INSERT) --- ## 1. 模块总览 - **页面 JSON 配置** - 表:`hw_web`,实体:`HwWeb` - 表:`hw_web1`,实体:`HwWeb1` - 作用:存储官网页面的 JSON 配置(含中英文),`hw_web1` 在 `hw_web` 基础上增加了 `deviceId`、`typeId` 维度,实现「页面编码 + 设备 + 类型」组合唯一。 - **官网菜单** - 表:`hw_web_menu`,实体:`HwWebMenu` - 表:`hw_web_menu1`,实体:`HwWebMenu1` - 作用:存储官网菜单树结构,支持多层级父子关系;其中 `HwWebMenu1` 相比 `HwWebMenu` 额外包含 `valuel` 字段(数据库列名为 `value`),用于绑定前端路由值或业务编码。 - **资料文件(带密钥保护)** - 表:`hw_web_document`,实体:`HwWebDocument` - 作用:存储资料文件的访问地址、所属页面编码、类型等,同时支持可选密钥保护(有密钥就必须验证才能拿到真实地址)。 - **软删除统一约定** - 所有上述表都使用 `is_delete` 字段做逻辑删除: - `'0'`:未删除 - `'1'`:已删除 - Mapper XML 中所有查询均默认附带 `and is_delete = '0'` 条件。 --- ## 2. 页面 JSON:HwWeb / HwWeb1 ### 2.1 实体结构 - **`HwWeb`(表:`hw_web`)** - `webId`:主键 ID - `webJson`:JSON 内容 - `webJsonString`:JSON 的字符串表示(便于展示或简单查询) - `webCode`:页面编码,作为业务主键 - `isDelete`:逻辑删除标志 - `webJsonEnglish`:英文 JSON 字符串 - **`HwWeb1`(表:`hw_web1`)** - 在 `HwWeb` 的基础上新增: - `deviceId`:设备 ID - `typeId`:类型 ID - 唯一约束在服务层通过 `(webCode, deviceId, typeId)` 组合保证。 **字段类型与数据库映射:** - `HwWeb`: | 字段名 | Java 类型 | 数据库列名 | 说明 | |--------|-----------|------------|------| | `webId` | `Long` | `web_id` | 主键 ID | | `webJson` | `String` | `web_json` | 页面 JSON 内容 | | `webJsonString` | `String` | `web_json_string` | JSON 的字符串形式 | | `webCode` | `Long` | `web_code` | 页面编码,业务主键 | | `isDelete` | `String` | `is_delete` | 逻辑删除标志,`0` 未删,`1` 已删 | | `webJsonEnglish` | `String` | `web_json_english` | 英文 JSON 字符串 | - `HwWeb1`: | 字段名 | Java 类型 | 数据库列名 | 说明 | |--------|-----------|------------|------| | `webId` | `Long` | `web_id` | 主键 ID | | `webJson` | `String` | `web_json` | 页面 JSON 内容 | | `webJsonString` | `String` | `web_json_string` | JSON 的字符串形式 | | `webCode` | `Long` | `web_code` | 页面编码,业务主键 | | `deviceId` | `Long` | `device_id` | 设备 ID,用于区分终端 | | `typeId` | `Long` | `typeId` | 类型 ID,用于区分业务/场景 | | `isDelete` | `String` | `is_delete` | 逻辑删除标志,`0` 未删,`1` 已删 | | `webJsonEnglish` | `String` | `web_json_english` | 英文 JSON 字符串 | ### 2.2 Mapper & XML 行为 #### 2.2.1 `HwWebMapper` + `HwWebMapper.xml` - **查询列表**:`selectHwWebList(HwWeb hwWeb)` - 基础 SQL:`select web_id, web_json, ... from hw_web` - 统一条件:`and is_delete = '0'` - 支持按 `webId`、`webJson`、`webJsonString`、`webCode`、`webJsonEnglish` 等过滤。 - **单条查询**:`selectHwWebByWebcode(Long webCode)` - 在基础 SQL 上追加:`where is_delete = '0' and web_code = #{webCode}` - **插入**:`insertHwWeb(HwWeb hwWeb)` - 按字段非空插入,并默认 `is_delete = '0'` - **更新**:`updateHwWeb(HwWeb hwWeb)` - 只允许更新 `web_json`、`web_json_string`、`web_json_english`,不更新 `web_code` 和主键。 - 更新条件为:`where web_code = #{webCode}`。 - **删除 / 批量删除** - `deleteHwWebByWebId(Long webId)`:`update hw_web set is_delete = '1' where web_id = #{webId}` - `deleteHwWebByWebIds(Long[] webIds)`:`update ... where web_id in (...)`,同样为逻辑删除。 #### 2.2.2 `HwWebMapper1` + `HwWebMapper1.xml` - 行为与 `HwWebMapper` 类似,差异: - 额外字段 `device_id`、`typeId`、`web_json_english`。 - `selectHwWebOne(HwWeb1 hwWeb1)`:按 `web_code + device_id + typeId` 精确查询一条记录。 - `updateHwWeb(HwWeb1 hwWeb1)` 的 where 条件为: - `where web_code = #{webCode} and device_id = #{deviceId} and typeId = #{typeId}` ### 2.3 Service 接口与实现 #### 2.3.1 接口:`IHwWebService` / `IHwWebService1` - 统一约定的方法: - `selectHwWebByWebcode(Long webCode)` - `selectHwWebList(...)` - `insertHwWeb(...)` - `updateHwWeb(...)` - `deleteHwWebByWebIds(Long[] webIds)` - `deleteHwWebByWebId(Long webId)` - `IHwWebService1` 额外提供:`selectHwWebOne(HwWeb1 hwWeb1)` #### 2.3.2 实现:`HwWebServiceImpl`(基于 `hw_web`) - **查询** - `selectHwWebByWebcode`:直接委托 Mapper,对 `webCode` 进行精确查询。 - `selectHwWebList`:委托 Mapper,按入参条件筛选列表。 - **新增** - `insertHwWeb`:调用 Mapper 插入,未做额外业务校验。 - **更新(逻辑:版本化替换)** - `updateHwWeb(HwWeb hwWeb)`: - 构造 `codeWeb`,只设置 `webCode`。 - 调用 `selectHwWebList(codeWeb)` 查出该编码下所有未删除记录。 - 若存在: - 提取这些记录的 `webId`,组装为数组。 - 调用 `deleteHwWebByWebIds` 对这些旧记录做「逻辑删除」。 - 为新纪录显式设置 `isDelete = "0"`。 - 调用 `insertHwWeb(hwWeb)` 插入一条**新的**记录(而不是更新原来的主键)。 - **效果**: - 每次更新页面 JSON,都会为同一个 `webCode` 生成一个新版本的记录,同时逻辑删除旧版本。 - 可保留历史记录(在数据库中),但系统只会查询 `is_delete = '0'` 的最新版本。 - **删除** - `deleteHwWebByWebIds` / `deleteHwWebByWebId`:通过 Mapper 将记录逻辑删除。 #### 2.3.3 实现:`HwWebServiceImpl1`(基于 `hw_web1`) - 与 `HwWebServiceImpl` 基本一致,差异在于**唯一键逻辑**: - 更新时构造 `codeWeb`: - `codeWeb.setWebCode(hwWeb1.getWebCode())`; - `codeWeb.setTypeId(hwWeb1.getTypeId())`; - `codeWeb.setDeviceId(hwWeb1.getDeviceId())`; - 调用 `selectHwWebList(codeWeb)` 按「页面编码 + 设备 + 类型」三元组过滤。 - 若存在老记录,同样调用 `deleteHwWebByWebIds` 做逻辑删除,然后插入一条新的记录,并设置 `isDelete = "0"`。 - 这样保证了 (`webCode`, `deviceId`, `typeId`) 组合在「未删除记录」中唯一,并且每次更新产生新版本。 ### 2.4 Controller:`HwWebController` 基路径:`/hwWeb` - **GET `/hwWeb/list`** - 入参:`HwWeb`(作为查询条件)。 - 调用:`hwWebService.selectHwWebList(hwWeb)`。 - 返回:`TableDataInfo`(分页表格数据)。 - **POST `/hwWeb/export`** - 导出 JSON 配置列表到 Excel,使用 `ExcelUtil`。 - **GET `/hwWeb/{webCode}`** - 通过页面编码 `webCode` 查询单条 JSON 配置。 - **POST `/hwWeb`** - 新增 JSON 配置,直接调用 `insertHwWeb`。 - **PUT `/hwWeb`** - 更新 JSON 配置,调用 `updateHwWeb`: - 实际为「逻辑删除旧版本 + 插入新版本」。 - **DELETE `/hwWeb/{webIds}`** - 批量逻辑删除。 - **GET `/hwWeb/getHwWebList`** - 返回 JSON 配置列表,包一层 `AjaxResult.success(...)`。 --- ## 3. 官网菜单:HwWebMenu / HwWebMenu1 ### 3.1 实体结构 两者都继承自 `TreeEntity`,支持树形结构字段:`ancestors`、`children` 等。 - **公共字段**(`HwWebMenu`、`HwWebMenu1` 均有): - `webMenuId`:菜单主键 ID - `parent`:父节点 ID(根节点为 `null` 或 `0`) - `status`:状态 - `webMenuName`:菜单名称 - `tenantId`:租户 ID - `webMenuPic`:图片地址 - `webMenuType`:菜单类型 - `isDelete`:逻辑删除标志 - `webMenuNameEnglish`:英文名称 - **`HwWebMenu1` 特有字段** - `valuel`:对应数据库中的 `value` 字段(字段名有一处 `valuel`,应理解为 value),用于携带菜单值(如路由或业务键)。 **字段类型与数据库列映射:** - `HwWebMenu`: | 字段名 | Java 类型 | 数据库列名 | 说明 | |--------|-----------|------------|------| | `webMenuId` | `Long` | `web_menu_id` | 菜单主键 ID | | `parent` | `Long` | `parent` | 父节点 ID(根节点为 0 或 NULL) | | `status` | `String` | `status` | 状态 | | `webMenuName` | `String` | `web_menu_name` | 菜单名称 | | `tenantId` | `Long` | `tenant_id` | 租户 ID | | `webMenuPic` | `String` | `web_menu__pic` | 图片地址 | | `webMenuType` | `Long` | `web_menu_type` | 菜单类型 | | `isDelete` | `String` | `is_delete` | 逻辑删除标志,`0` 未删,`1` 已删 | | `webMenuNameEnglish` | `String` | `web_menu_name_english` | 英文菜单名称 | - `HwWebMenu1`: | 字段名 | Java 类型 | 数据库列名 | 说明 | |--------|-----------|------------|------| | `webMenuId` | `Long` | `web_menu_id` | 菜单主键 ID | | `parent` | `Long` | `parent` | 父节点 ID(根节点为 0 或 NULL) | | `status` | `String` | `status` | 状态 | | `webMenuName` | `String` | `web_menu_name` | 菜单名称 | | `tenantId` | `Long` | `tenant_id` | 租户 ID | | `webMenuPic` | `String` | `web_menu__pic` | 图片地址 | | `webMenuType` | `Long` | `web_menu_type` | 菜单类型 | | `valuel` | `String` | `value` | 菜单绑定值(如路由、业务编码);实体字段名为 `valuel` | | `isDelete` | `String` | `is_delete` | 逻辑删除标志,`0` 未删,`1` 已删 | | `webMenuNameEnglish` | `String` | `web_menu_name_english` | 英文菜单名称 | ### 3.2 Mapper & XML 行为 #### 3.2.1 `HwWebMenuMapper` / `HwWebMenuMapper.xml` - 查询列表 `selectHwWebMenuList(HwWebMenu hwWebMenu)`: - 统一条件:`and is_delete = '0'`。 - 支持按 `parent`、`ancestors`、`status`、`webMenuName (like)`、`tenantId`、`webMenuPic`、`webMenuType`、`webMenuNameEnglish (like)` 等过滤。 - 单条查询 `selectHwWebMenuByWebMenuId(Long webMenuId)`:按主键 ID + `is_delete = '0'` 查询。 - 插入 / 更新 / 删除: - 插入时按字段非空写入,并默认 `is_delete = '0'`。 - 更新仅更新业务字段,不改变 `is_delete`。 - 删除/批量删除采用 `update ... set is_delete = '1'` 实现软删除。 #### 3.2.2 `HwWebMenuMapper1` / `HwWebMenuMapper1.xml` - 与 `HwWebMenuMapper` 基本一致,差异: - 多了 `value` 字段(对应实体的 `valuel`)。 - 查询列表时可按 `value like` 模糊匹配。 ### 3.3 Service:菜单树构建算法 `HwWebMenuServiceImpl` 与 `HwWebMenuServiceImpl1` 的结构几乎一致,只是实体类型和 Mapper 不同。核心逻辑: #### 3.3.1 列表 & 新增/修改/删除 - `selectHwWebMenuList`:直接委托 Mapper,对应接口方法。 - **新增**:`insertHwWebMenu`: - 调用 Mapper 插入,未做额外业务校验。 - **更新**:`updateHwWebMenu`: - 调用 Mapper 更新,仅更新业务字段,不改变 `is_delete`。 - **删除**:`deleteHwWebMenuByWebMenuId` / `deleteHwWebMenuByWebMenuIds`: - 调用 Mapper 删除/批量删除,实现软删除。 #### 3.3.2 构建菜单树:`selectMenuTree` + `buildWebMenuTree` - **入口**: - `selectMenuTree(HwWebMenu hwWebMenu)` / `selectMenuTree(HwWebMenu1 hwWebMenu1)`: - 先查询平铺列表:`hwWebMenuMapper.selectHwWebMenuList(...)`。 - 调用 `buildWebMenuTree(List<...> menus)` 构造树结构。 - **`buildWebMenuTree` 逻辑**(两者完全类似): 1. 收集所有菜单的 `webMenuId` 到列表 `tempList`。 2. 遍历每个菜单 `menu`: - 若满足以下任一条件,则视为根节点: - `menu.getParent() == null` - `menu.getParent() == 0L` - `tempList` 中不包含 `menu.getParent()`(父节点不在当前集合中) - 对于根节点调用 `recursionFn(menus, menu)` 递归设置子节点,并加入返回列表 `returnList`。 3. 若最终 `returnList` 为空(极端情况),则直接返回原始 `menus`。 - **`recursionFn` 递归设置子节点**: - 通过 `getChildList(list, t)` 获取所有 `parent == t.webMenuId` 的子菜单列表。 - 调用 `t.setChildren(childList)` 建立树结构。 - 对每个子节点 `tChild`,若 `hasChild(list, tChild)` 返回 `true`,则继续递归。 - **`getChildList` / `hasChild`**: - `getChildList` 遍历整张平铺列表,筛选 `parent` 等于当前节点 ID 的记录。 - `hasChild` 则判断 `getChildList` 返回列表是否为空。 - 使用 `StringUtils.isNotNull(n.getParent())` 判断父节点不为空,提高健壮性。 > 前端只需要调用 `/hwWebMenu/selectMenuTree` 或 `/hwWebMenu1/selectMenuTree`,即可得到带 `children` 字段的完整菜单树结构。 ### 3.4 Controller:菜单接口 #### 3.4.1 旧菜单:`HwWebMenuController`(`/hwWebMenu`) - **GET `/hwWebMenu/list`**:返回菜单列表(平铺)。 - **POST `/hwWebMenu/export`**:导出 Excel。 - **GET `/hwWebMenu/{webMenuId}`**:按 ID 查询明细。 - **POST `/hwWebMenu`**:新增菜单。 - **PUT `/hwWebMenu`**:修改菜单。 - **DELETE `/hwWebMenu/{webMenuIds}`**:批量删除(软删除)。 - **GET `/hwWebMenu/selectMenuTree`**:返回菜单树。 #### 3.4.2 新菜单:`HwWebMenuController1`(`/hwWebMenu1`) - 与 `HwWebMenuController` 完全对齐,只是实体类型换成 `HwWebMenu1`,以及调用 `IHwWebMenuService1`。 --- ## 4. 资料文件与密钥访问:HwWebDocument ### 4.1 实体结构:`HwWebDocument`(表:`hw_web_document`) - `documentId`:主键(`String` 类型)。 - `tenantId`:租户 ID。 - `documentAddress`:真实文件存储地址(URL 或路径)。 - `webCode`:页面编码,用于和页面/菜单做关联。 - `secretKey`:访问密钥。 - 使用 `@JsonProperty(access = WRITE_ONLY)`,序列化时不会返回给前端。 - `json`:附加 JSON 信息(例如文件元数据)。 - `type`:文件类型。(如 pdf、doc 等) - `isDelete`:逻辑删除标志。 - 衍生方法:`getHasSecret()` 返回是否配置了密钥(用于前端判断是否需要弹出输入密钥的对话框)。 **字段类型与数据库列映射:** | 字段名 | Java 类型 | 数据库列名 | 说明 | |--------|-----------|------------|------| | `documentId` | `String` | `document_id` | 主键 ID(字符串类型) | | `tenantId` | `Long` | `tenant_id` | 租户 ID | | `documentAddress` | `String` | `document_address` | 文件存储地址(URL 或路径) | | `webCode` | `String` | `web_code` | 页面编码,用于连表或关联页面 | | `secretKey` | `String` | `secretKey` | 访问密钥(仅写入,序列化时不返回) | | `json` | `String` | `json` | 附加 JSON 信息(如文件元数据) | | `type` | `String` | `type` | 文件类型(如 pdf、doc) | | `isDelete` | `String` | `is_delete` | 逻辑删除标志,`0` 未删,`1` 已删 | ### 4.2 Mapper & XML 行为 - 列表查询 `selectHwWebDocumentList`: - 强制 `and is_delete = '0'`。 - 支持按 `documentId`、`tenantId`、`documentAddress`、`webCode`、`secretKey`、`json`、`type` 等过滤。 - 单条查询 `selectHwWebDocumentByDocumentId`:按主键 + `is_delete = '0'` 查询。 - 插入 `insertHwWebDocument`: - 按字段非空插入,并默认 `is_delete = '0'`。 - 更新 `updateHwWebDocument` 的特殊处理: - 对 `secretKey` 字段使用: ```xml secretKey = NULL #{secretKey} , ``` - 配合 Service 层的「null 转空串」逻辑,实现: - **前端不传或传 null**:认为要清空数据库中的密钥(置为 `NULL`)。 - **前端传非空字符串**:更新为新的密钥。 - 删除 / 批量删除: - `deleteHwWebDocumentByDocumentId` / `deleteHwWebDocumentByDocumentIds`:逻辑删除(`is_delete = '1'`)。 ### 4.3 Service 实现:`HwWebDocumentServiceImpl` - **查询/列表/删除**:直接委托 Mapper,对应接口方法。 - **新增**:`insertHwWebDocument`: - 在插入前设置 `createTime = DateUtils.getNowDate()`。 - **更新**:`updateHwWebDocument`: - 如果 `hwWebDocument.getSecretKey() == null`,则强制 `setSecretKey("")`: - 触发 Mapper 中 `` 条件。 - 在 XML 中根据空串设置数据库列为 `NULL`,从而**清空密钥**。 - 若前端传入非空密钥字符串,则按正常更新覆盖原密钥。 - **密钥验证与安全地址获取**:`verifyAndGetDocumentAddress(String documentId, String providedKey)` 1. 根据 `documentId` 查询文档,若为空:抛出 `ServiceException("文件不存在")`。 2. 取出 `secretKey` 与 `documentAddress`。 3. 若 `secretKey` 为空或空白: - 说明此文件不需要密钥,直接返回 `documentAddress`。 4. 若 `secretKey` 非空: - 将入参 `providedKey` 去空格;若为空:抛出 `ServiceException("密钥不能为空")`。 - 若 `secretKey.trim().equals(trimmedProvided)`:返回 `documentAddress`。 - 否则抛出 `ServiceException("密钥错误")`。 > 因为这里抛出的都是 `ServiceException`,Controller 层可统一捕获并将 `e.getMessage()` 作为错误提示返回给前端。 ### 4.4 Controller:`HwWebDocumentController`(`/hwWebDocument`) - **GET `/hwWebDocument/list`** - 调用 `startPage()` 分页。 - 查询列表后,对每个 `HwWebDocument doc` 做二次处理: - `doc.setSecretKey(null)`:隐藏密钥字段。 - 若 `doc.getHasSecret()` 为 `true`:`doc.setDocumentAddress(null)`,隐藏真实文件地址,只暴露「是否加密」。 - 通过 `getDataTable(list)` 返回分页数据。 - **POST `/hwWebDocument/export`** - 导出列表到 Excel。 - **GET `/hwWebDocument/{documentId}`** - 查询单条记录,并同样隐藏密钥字段;如果有密钥,也会隐藏 `documentAddress`。 - **POST `/hwWebDocument`** - 新增文档记录。 - **PUT `/hwWebDocument`** - 修改文档记录,调用 `updateHwWebDocument`。通过 Service + XML 的联合逻辑实现「设置/修改/清空密钥」。 - **DELETE `/hwWebDocument/{documentIds}`** - 批量逻辑删除。 - **POST `/hwWebDocument/getSecureDocumentAddress`** - 入参:`SecureDocumentRequest`(包含 `documentId` 和 `providedKey`)。 - 调用 `hwWebDocumentService.verifyAndGetDocumentAddress(...)`: - 成功:返回真实 `documentAddress`。 - 失败:捕获异常,返回 `error(e.getMessage())`,前端可展示友好提示(文件不存在 / 密钥不能为空 / 密钥错误等)。 --- ## 5. 典型业务流程梳理 ### 5.1 更新页面 JSON(HwWeb/HwWeb1) 1. 后台管理前端提交新的 JSON 配置到: - `PUT /hwWeb`(基于 `hw_web`),或 - `PUT /hwWeb1` 对应的 Controller(若有,对应 `HwWebServiceImpl1`)。 2. Service 层根据页面编码(以及 `deviceId`、`typeId`)查询已存在记录。 3. 将旧记录全部逻辑删除(`is_delete = '1'`)。 4. 插入一条新记录,`isDelete = '0'`。 5. 前端访问页面时,只会根据编码查询到最新一条记录。 ### 5.2 构建官网菜单树(HwWebMenu/HwWebMenu1) 1. 前端调用: - 旧菜单:`GET /hwWebMenu/selectMenuTree` - 新菜单:`GET /hwWebMenu1/selectMenuTree` 2. Service 调用 Mapper 查询 **平铺列表**(均为 `is_delete = '0'` 的记录)。 3. 通过 `buildWebMenuTree` 按 `parent` 字段递归构建树形结构: - 识别所有根节点(`parent == null/0` 或父节点不在列表中)。 - 递归为每个节点挂接 `children`。 4. 返回给前端一个包含 `children` 的树形菜单结构,用于渲染导航栏或侧边栏。 ### 5.3 受密钥保护的文件访问(HwWebDocument) 1. 后台配置文件记录时可以选择是否设置 `secretKey`: - 不设置:文件公开可访问。 - 设置:文件地址将被后端隐藏,需要密钥校验。 2. 前端先调用 `GET /hwWebDocument/list` 或 `GET /hwWebDocument/{documentId}` 获取文件列表/详情: - 只会看到 `hasSecret`(通过 `getHasSecret()` 间接体现),看不到真实地址。 3. 用户在前端输入密钥后,前端调用: - `POST /hwWebDocument/getSecureDocumentAddress`,携带 `documentId` 与 `providedKey`。 4. Service 层校验密钥: - 无密钥:直接返回地址。 - 密钥为空:抛错 `"密钥不能为空"`。 - 密钥不匹配:抛错 `"密钥错误"`。 5. 前端拿到真实地址后,可发起真正的文件下载/打开请求。 --- ## 6. 核心源码(Controller + Service 实现) > 以下为与 `HwWeb / HwWeb1 / HwWebDocument / HwWebMenu / HwWebMenu1` 强相关的控制层和服务实现类的完整源码,方便 AI 与人工检索、分析。实体类、接口与 Mapper XML 已在上文详细说明,可在 IDE 中直接定位查看原文件。 ### 6.1 Controller 源码 #### 6.1.1 `HwWebController` ```java package com.ruoyi.portal.controller; import java.util.List; import java.io.IOException; import javax.servlet.http.HttpServletResponse; import com.ruoyi.portal.domain.HwWeb1; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.ruoyi.common.log.annotation.Log; import com.ruoyi.common.log.enums.BusinessType; import com.ruoyi.common.security.annotation.RequiresPermissions; import com.ruoyi.portal.domain.HwWeb; import com.ruoyi.portal.service.IHwWebService; import com.ruoyi.common.core.web.controller.BaseController; import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.core.utils.poi.ExcelUtil; import com.ruoyi.common.core.web.page.TableDataInfo; /** * haiwei官网jsonController * * @author ruoyi * @date 2025-08-18 */ @RestController @RequestMapping("/hwWeb") public class HwWebController extends BaseController { @Autowired private IHwWebService hwWebService; /** * 查询haiwei官网json列表 */ //@RequiresPermissions("portalhwWeb:list") @GetMapping("/list") public TableDataInfo list(HwWeb hwWeb) { // startPage(); List list = hwWebService.selectHwWebList(hwWeb); return getDataTable(list); } /** * 导出haiwei官网json列表 */ //@RequiresPermissions("portalhwWeb:export") //@Log(title = "haiwei官网json", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(HttpServletResponse response, HwWeb hwWeb) { List list = hwWebService.selectHwWebList(hwWeb); ExcelUtil util = new ExcelUtil(HwWeb.class); util.exportExcel(response, list, "haiwei官网json数据"); } /** * 获取haiwei官网json详细信息 */ //@RequiresPermissions("portalhwWeb:query") @GetMapping(value = "/{webCode}") public AjaxResult getInfo(@PathVariable("webCode") Long webCode) { return success(hwWebService.selectHwWebByWebcode(webCode)); } /** * 新增haiwei官网json */ //@RequiresPermissions("portalhwWeb:add") //@Log(title = "haiwei官网json", businessType = BusinessType.INSERT) @PostMapping public AjaxResult add(@RequestBody HwWeb hwWeb) { return toAjax(hwWebService.insertHwWeb(hwWeb)); } /** * 修改haiwei官网json */ //@RequiresPermissions("portalhwWeb:edit") //@Log(title = "haiwei官网json", businessType = BusinessType.UPDATE) @PutMapping public AjaxResult edit(@RequestBody HwWeb hwWeb) { int i = hwWebService.updateHwWeb(hwWeb); return toAjax(i); } /** * 删除haiwei官网json */ //@RequiresPermissions("portalhwWeb:remove") //@Log(title = "haiwei官网json", businessType = BusinessType.DELETE) @DeleteMapping("/{webIds}") public AjaxResult remove(@PathVariable Long[] webIds) { return toAjax(hwWebService.deleteHwWebByWebIds(webIds)); } @GetMapping("/getHwWebList") public AjaxResult getHwWebList(HwWeb HwWeb) { return success(hwWebService.selectHwWebList(HwWeb)) ; } } ``` #### 6.1.2 `HwWebMenuController` ```java package com.ruoyi.portal.controller; import java.util.List; import java.io.IOException; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.ruoyi.common.log.annotation.Log; import com.ruoyi.common.log.enums.BusinessType; import com.ruoyi.common.security.annotation.RequiresPermissions; import com.ruoyi.portal.domain.HwWebMenu; import com.ruoyi.portal.service.IHwWebMenuService; import com.ruoyi.common.core.web.controller.BaseController; import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.core.utils.poi.ExcelUtil; /** * haiwei官网菜单Controller * * @author zch * @date 2025-08-18 */ @RestController @RequestMapping("/hwWebMenu") public class HwWebMenuController extends BaseController { @Autowired private IHwWebMenuService hwWebMenuService; /** * 查询haiwei官网菜单列表 */ //@RequiresPermissions("portalhwWebMenu:list") @GetMapping("/list") public AjaxResult list(HwWebMenu hwWebMenu) { List list = hwWebMenuService.selectHwWebMenuList(hwWebMenu); return success(list); } /** * 导出haiwei官网菜单列表 */ //@RequiresPermissions("portalhwWebMenu:export") //@Log(title = "haiwei官网菜单", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(HttpServletResponse response, HwWebMenu hwWebMenu) { List list = hwWebMenuService.selectHwWebMenuList(hwWebMenu); ExcelUtil util = new ExcelUtil(HwWebMenu.class); util.exportExcel(response, list, "haiwei官网菜单数据"); } /** * 获取haiwei官网菜单详细信息 */ //@RequiresPermissions("portalhwWebMenu:query") @GetMapping(value = "/{webMenuId}") public AjaxResult getInfo(@PathVariable("webMenuId") Long webMenuId) { return success(hwWebMenuService.selectHwWebMenuByWebMenuId(webMenuId)); } /** * 新增haiwei官网菜单 */ //@RequiresPermissions("portalhwWebMenu:add") //@Log(title = "haiwei官网菜单", businessType = BusinessType.INSERT) @PostMapping public AjaxResult add(@RequestBody HwWebMenu hwWebMenu) { return toAjax(hwWebMenuService.insertHwWebMenu(hwWebMenu)); } /** * 修改haiwei官网菜单 */ //@RequiresPermissions("portalhwWebMenu:edit") //@Log(title = "haiwei官网菜单", businessType = BusinessType.UPDATE) @PutMapping public AjaxResult edit(@RequestBody HwWebMenu hwWebMenu) { return toAjax(hwWebMenuService.updateHwWebMenu(hwWebMenu)); } /** * 删除haiwei官网菜单 */ //@RequiresPermissions("portalhwWebMenu:remove") //@Log(title = "haiwei官网菜单", businessType = BusinessType.DELETE) @DeleteMapping("/{webMenuIds}") public AjaxResult remove(@PathVariable Long[] webMenuIds) { return toAjax(hwWebMenuService.deleteHwWebMenuByWebMenuIds(webMenuIds)); } /** * 获取菜单树列表 */ @GetMapping("/selectMenuTree") public AjaxResult selectMenuTree(HwWebMenu hwWebMenu){ return success(hwWebMenuService.selectMenuTree(hwWebMenu)); } } ``` #### 6.1.3 `HwWebMenuController1` ```java package com.ruoyi.portal.controller; import com.ruoyi.common.core.utils.poi.ExcelUtil; import com.ruoyi.common.core.web.controller.BaseController; import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.log.annotation.Log; import com.ruoyi.common.log.enums.BusinessType; import com.ruoyi.portal.domain.HwWebMenu1; import com.ruoyi.portal.service.IHwWebMenuService1; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * haiwei官网菜单Controller * * @author zch * @date 2025-08-18 */ @RestController @RequestMapping("/hwWebMenu1") public class HwWebMenuController1 extends BaseController { @Autowired private IHwWebMenuService1 hwWebMenuService1; /** * 查询haiwei官网菜单列表 */ //@RequiresPermissions("portalhwWebMenu:list") @GetMapping("/list") public AjaxResult list(HwWebMenu1 hwWebMenu1) { List list = hwWebMenuService1.selectHwWebMenuList(hwWebMenu1); return success(list); } /** * 导出haiwei官网菜单列表 */ //@RequiresPermissions("portalhwWebMenu:export") //@Log(title = "haiwei官网菜单", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(HttpServletResponse response, HwWebMenu1 hwWebMenu1) { List list = hwWebMenuService1.selectHwWebMenuList(hwWebMenu1); ExcelUtil util = new ExcelUtil(HwWebMenu1.class); util.exportExcel(response, list, "haiwei官网菜单数据"); } /** * 获取haiwei官网菜单详细信息 */ //@RequiresPermissions("portalhwWebMenu:query") @GetMapping(value = "/{webMenuId}") public AjaxResult getInfo(@PathVariable("webMenuId") Long webMenuId) { return success(hwWebMenuService1.selectHwWebMenuByWebMenuId(webMenuId)); } /** * 新增haiwei官网菜单 */ //@RequiresPermissions("portalhwWebMenu:add") //@Log(title = "haiwei官网菜单", businessType = BusinessType.INSERT) @PostMapping public AjaxResult add(@RequestBody HwWebMenu1 hwWebMenu1) { return toAjax(hwWebMenuService1.insertHwWebMenu(hwWebMenu1)); } /** * 修改haiwei官网菜单 */ //@RequiresPermissions("portalhwWebMenu:edit") //@Log(title = "haiwei官网菜单", businessType = BusinessType.UPDATE) @PutMapping public AjaxResult edit(@RequestBody HwWebMenu1 hwWebMenu1) { return toAjax(hwWebMenuService1.updateHwWebMenu(hwWebMenu1)); } /** * 删除haiwei官网菜单 */ //@RequiresPermissions("portalhwWebMenu:remove") //@Log(title = "haiwei官网菜单", businessType = BusinessType.DELETE) @DeleteMapping("/{webMenuIds}") public AjaxResult remove(@PathVariable Long[] webMenuIds) { return toAjax(hwWebMenuService1.deleteHwWebMenuByWebMenuIds(webMenuIds)); } /** * 获取菜单树列表 */ @GetMapping("/selectMenuTree") public AjaxResult selectMenuTree(HwWebMenu1 hwWebMenu1){ return success(hwWebMenuService1.selectMenuTree(hwWebMenu1)); } } ``` #### 6.1.4 `HwWebDocumentController` ```java package com.ruoyi.portal.controller; import java.util.List; import java.io.IOException; import javax.servlet.http.HttpServletResponse; import com.ruoyi.portal.domain.SecureDocumentRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.ruoyi.common.log.annotation.Log; import com.ruoyi.common.log.enums.BusinessType; import com.ruoyi.common.security.annotation.RequiresPermissions; import com.ruoyi.portal.domain.HwWebDocument; import com.ruoyi.portal.service.IHwWebDocumentService; import com.ruoyi.common.core.web.controller.BaseController; import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.core.utils.poi.ExcelUtil; import com.ruoyi.common.core.web.page.TableDataInfo; /** * Hw资料文件Controller * * @author zch * @date 2025-09-22 */ @RestController @RequestMapping("/hwWebDocument") public class HwWebDocumentController extends BaseController { @Autowired private IHwWebDocumentService hwWebDocumentService; /** * 查询Hw资料文件列表 */ // @RequiresPermissions("portal:hwWebDocument:list") @GetMapping("/list") public TableDataInfo list(HwWebDocument hwWebDocument) { startPage(); List list = hwWebDocumentService.selectHwWebDocumentList(hwWebDocument); for (HwWebDocument doc : list) { // 隐藏密钥,若设置了密钥则隐藏文件地址 doc.setSecretKey(null); if (doc.getHasSecret()) { doc.setDocumentAddress(null); } } return getDataTable(list); } /** * 导出Hw资料文件列表 */ // @RequiresPermissions("portal:hwWebDocument:export") //@Log(title = "Hw资料文件", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(HttpServletResponse response, HwWebDocument hwWebDocument) { List list = hwWebDocumentService.selectHwWebDocumentList(hwWebDocument); ExcelUtil util = new ExcelUtil(HwWebDocument.class); util.exportExcel(response, list, "Hw资料文件数据"); } /** * 获取Hw资料文件详细信息 */ // @RequiresPermissions("portal:hwWebDocument:query") @GetMapping(value = "/{documentId}") public AjaxResult getInfo(@PathVariable("documentId") String documentId) { HwWebDocument doc = hwWebDocumentService.selectHwWebDocumentByDocumentId(documentId); if (doc != null) { // 隐藏密钥,若设置了密钥则隐藏文件地址 doc.setSecretKey(null); if (doc.getHasSecret()) { doc.setDocumentAddress(null); } } return success(doc); } /** * 新增Hw资料文件 */ // @RequiresPermissions("portal:hwWebDocument:add") //@Log(title = "Hw资料文件", businessType = BusinessType.INSERT) @PostMapping public AjaxResult add(@RequestBody HwWebDocument hwWebDocument) { return toAjax(hwWebDocumentService.insertHwWebDocument(hwWebDocument)); } /** * 修改Hw资料文件 */ // @RequiresPermissions("portal:hwWebDocument:edit") //@Log(title = "Hw资料文件", businessType = BusinessType.UPDATE) @PutMapping public AjaxResult edit(@RequestBody HwWebDocument hwWebDocument) { System.out.println(hwWebDocument.getSecretKey()); return toAjax(hwWebDocumentService.updateHwWebDocument(hwWebDocument)); } /** * 删除Hw资料文件 */ // @RequiresPermissions("portal:hwWebDocument:remove") //@Log(title = "Hw资料文件", businessType = BusinessType.DELETE) @DeleteMapping("/{documentIds}") public AjaxResult remove(@PathVariable String[] documentIds) { return toAjax(hwWebDocumentService.deleteHwWebDocumentByDocumentIds(documentIds)); } /** * 获取安全文件地址 */ // @RequiresPermissions("portal:hwWebDocument:query") //@Log(title = "获取安全文件地址", businessType = BusinessType.OTHER) @PostMapping("/getSecureDocumentAddress") public AjaxResult getSecureDocumentAddress(@RequestBody SecureDocumentRequest request) { try { String address = hwWebDocumentService.verifyAndGetDocumentAddress(request.getDocumentId(), request.getProvidedKey()); return success(address); } catch (Exception e) { return error(e.getMessage()); } } } ``` ### 6.2 Service 实现源码 #### 6.2.1 `HwWebServiceImpl` ```java package com.ruoyi.portal.service.impl; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ruoyi.portal.mapper.HwWebMapper; import com.ruoyi.portal.domain.HwWeb; import com.ruoyi.portal.service.IHwWebService; import org.springframework.transaction.annotation.Transactional; /** * haiwei官网jsonService业务层处理 * * @author ruoyi * @date 2025-08-18 */ @Service public class HwWebServiceImpl implements IHwWebService { @Autowired private HwWebMapper hwWebMapper; /** * 查询haiwei官网json * * @param webId haiwei官网json主键 * @return haiwei官网json */ @Override public HwWeb selectHwWebByWebcode(Long webCode) { HwWeb hwWeb = hwWebMapper.selectHwWebByWebcode(webCode); return hwWeb; } /** * 查询haiwei官网json列表 * * @param hwWeb haiwei官网json * @return haiwei官网json */ @Override public List selectHwWebList(HwWeb hwWeb) { return hwWebMapper.selectHwWebList(hwWeb); } /** * 新增haiwei官网json * * @param hwWeb haiwei官网json * @return 结果 */ @Override public int insertHwWeb(HwWeb hwWeb) { return hwWebMapper.insertHwWeb(hwWeb); } /** * 修改haiwei官网json * * @param hwWeb haiwei官网json * @return 结果 */ @Override @Transactional( rollbackFor = Exception.class ) public int updateHwWeb(HwWeb hwWeb) { HwWeb codeWeb = new HwWeb(); //编号唯一 codeWeb.setWebCode(hwWeb.getWebCode()); List exists = hwWebMapper.selectHwWebList(codeWeb); if (!exists.isEmpty()) { Long[] webIds = exists.stream().map(HwWeb::getWebId).toArray(Long[]::new); //逻辑删除旧纪录 hwWebMapper.deleteHwWebByWebIds(webIds); } // 插入新记录,避免复用旧主键 // hwWeb.setWebId(null); hwWeb.setIsDelete("0"); return hwWebMapper.insertHwWeb(hwWeb); } /** * 批量删除haiwei官网json * * @param webIds 需要删除的haiwei官网json主键 * @return 结果 */ @Override public int deleteHwWebByWebIds(Long[] webIds) { return hwWebMapper.deleteHwWebByWebIds(webIds); } /** * 删除haiwei官网json信息 * * @param webId haiwei官网json主键 * @return 结果 */ @Override public int deleteHwWebByWebId(Long webId) { return hwWebMapper.deleteHwWebByWebId(webId); } } ``` #### 6.2.2 `HwWebServiceImpl1` ```java package com.ruoyi.portal.service.impl; import com.ruoyi.portal.mapper.HwWebMapper1; import com.ruoyi.portal.service.IHwWebService1; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import com.ruoyi.portal.domain.HwWeb1; import com.ruoyi.portal.domain.HwWeb; import org.springframework.transaction.annotation.Transactional; /** * haiwei官网jsonService业务层处理 * * @author ruoyi * @date 2025-08-18 */ @Service public class HwWebServiceImpl1 implements IHwWebService1 { @Autowired private HwWebMapper1 hwWebMapper1; /** * 查询haiwei官网json * * @param webId haiwei官网json主键 * @return haiwei官网json */ @Override public HwWeb1 selectHwWebByWebcode(Long webCode) { return hwWebMapper1.selectHwWebByWebcode(webCode); } @Override public HwWeb1 selectHwWebOne(HwWeb1 hwWeb1) { return hwWebMapper1.selectHwWebOne(hwWeb1); } /** * 查询haiwei官网json列表 * * @param HwWeb1 haiwei官网json * @return haiwei官网json */ @Override public List selectHwWebList(HwWeb1 hwWeb1) { return hwWebMapper1.selectHwWebList(hwWeb1); } /** * 新增haiwei官网json * * @param HwWeb1 haiwei官网json * @return 结果 */ @Override public int insertHwWeb(HwWeb1 hwWeb1) { return hwWebMapper1.insertHwWeb(hwWeb1); } /** * 修改haiwei官网json * * @param HwWeb1 haiwei官网json * @return 结果 */ @Override @Transactional( rollbackFor = Exception.class ) public int updateHwWeb(HwWeb1 hwWeb1) { HwWeb1 codeWeb = new HwWeb1(); // 编号、typeid、deviceID保证唯一 codeWeb.setWebCode(hwWeb1.getWebCode()); codeWeb.setTypeId(hwWeb1.getTypeId()); codeWeb.setDeviceId(hwWeb1.getDeviceId()); List exists = hwWebMapper1.selectHwWebList(codeWeb); if (!exists.isEmpty()) { Long[] webIds = exists.stream().map(HwWeb1::getWebId).toArray(Long[]::new); //逻辑删除旧纪录 hwWebMapper1.deleteHwWebByWebIds(webIds); } // 插入新记录,避免复用旧主键 // hwWeb1.setWebId(null); hwWeb1.setIsDelete("0"); return hwWebMapper1.insertHwWeb(hwWeb1); } /** * 批量删除haiwei官网json * * @param webIds 需要删除的haiwei官网json主键 * @return 结果 */ @Override public int deleteHwWebByWebIds(Long[] webIds) { return hwWebMapper1.deleteHwWebByWebIds(webIds); } /** * 删除haiwei官网json信息 * * @param webId haiwei官网json主键 * @return 结果 */ @Override public int deleteHwWebByWebId(Long webId) { return hwWebMapper1.deleteHwWebByWebId(webId); } } ``` #### 6.2.3 `HwWebMenuServiceImpl` ```java package com.ruoyi.portal.service.impl; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.portal.domain.HwProductInfoDetail; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ruoyi.portal.mapper.HwWebMenuMapper; import com.ruoyi.portal.domain.HwWebMenu; import com.ruoyi.portal.service.IHwWebMenuService; /** * haiwei官网菜单Service业务层处理 * * @author zch * @date 2025-08-18 */ @Service public class HwWebMenuServiceImpl implements IHwWebMenuService { @Autowired private HwWebMenuMapper hwWebMenuMapper; /** * 查询haiwei官网菜单 * * @param webMenuId haiwei官网菜单主键 * @return haiwei官网菜单 */ @Override public HwWebMenu selectHwWebMenuByWebMenuId(Long webMenuId) { return hwWebMenuMapper.selectHwWebMenuByWebMenuId(webMenuId); } /** * 查询haiwei官网菜单列表 * * @param hwWebMenu haiwei官网菜单 * @return haiwei官网菜单 */ @Override public List selectHwWebMenuList(HwWebMenu hwWebMenu) { List hwWebMenus = hwWebMenuMapper.selectHwWebMenuList(hwWebMenu); return hwWebMenus; } /** * 获取菜单树列表 */ @Override public List selectMenuTree(HwWebMenu hwWebMenu) { List hwWebMenus = hwWebMenuMapper.selectHwWebMenuList(hwWebMenu); return buildWebMenuTree(hwWebMenus); } /** * 新增haiwei官网菜单 * * @param hwWebMenu haiwei官网菜单 * @return 结果 */ @Override public int insertHwWebMenu(HwWebMenu hwWebMenu) { return hwWebMenuMapper.insertHwWebMenu(hwWebMenu); } /** * 修改haiwei官网菜单 * * @param hwWebMenu haiwei官网菜单 * @return 结果 */ @Override public int updateHwWebMenu(HwWebMenu hwWebMenu) { return hwWebMenuMapper.updateHwWebMenu(hwWebMenu); } /** * 批量删除haiwei官网菜单 * * @param webMenuIds 需要删除的haiwei官网菜单主键 * @return 结果 */ @Override public int deleteHwWebMenuByWebMenuIds(Long[] webMenuIds) { return hwWebMenuMapper.deleteHwWebMenuByWebMenuIds(webMenuIds); } /** * 删除haiwei官网菜单信息 * * @param webMenuId haiwei官网菜单主键 * @return 结果 */ @Override public int deleteHwWebMenuByWebMenuId(Long webMenuId) { return hwWebMenuMapper.deleteHwWebMenuByWebMenuId(webMenuId); } /** * 构建前端所需要树结构(根据传入的平铺菜单列表构造树) * * @param menus 菜单列表 * @return 树结构列表 */ public List buildWebMenuTree(List menus) { List returnList = new ArrayList<>(); List tempList = menus.stream().map(HwWebMenu::getWebMenuId).collect(Collectors.toList()); for (HwWebMenu menu : menus) { // 如果是顶级节点(parent为null、0或者不在当前列表中), 遍历该父节点的所有子节点 if (menu.getParent() == null || menu.getParent() == 0L || !tempList.contains(menu.getParent())) { recursionFn(menus, menu); returnList.add(menu); } } if (returnList.isEmpty()) { returnList = menus; } return returnList; } /** * 递归设置子节点 */ private void recursionFn(List list, HwWebMenu t) { // 得到子节点列表 List childList = getChildList(list, t); t.setChildren(childList); for (HwWebMenu tChild : childList) { if (hasChild(list, tChild)) { recursionFn(list, tChild); } } } /** * 得到子节点列表 */ private List getChildList(List list, HwWebMenu t) { List tlist = new ArrayList(); Iterator it = list.iterator(); while (it.hasNext()) { HwWebMenu n = it.next(); if (StringUtils.isNotNull(n.getParent()) && n.getParent().longValue() == t.getWebMenuId().longValue()) { tlist.add(n); } } return tlist; } /** * 判断是否有子节点 */ private boolean hasChild(List list, HwWebMenu t) { return !getChildList(list, t).isEmpty(); } } ``` #### 6.2.4 `HwWebMenuServiceImpl1` ```java package com.ruoyi.portal.service.impl; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.portal.domain.HwWebMenu1; import com.ruoyi.portal.mapper.HwWebMenuMapper1; import com.ruoyi.portal.service.IHwWebMenuService1; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; /** * haiwei官网菜单Service业务层处理 * * @author zch * @date 2025-08-18 */ @Service public class HwWebMenuServiceImpl1 implements IHwWebMenuService1 { @Autowired private HwWebMenuMapper1 HwWebMenuMapper1; /** * 查询haiwei官网菜单 * * @param webMenuId haiwei官网菜单主键 * @return haiwei官网菜单 */ @Override public HwWebMenu1 selectHwWebMenuByWebMenuId(Long webMenuId) { return HwWebMenuMapper1.selectHwWebMenuByWebMenuId(webMenuId); } /** * 查询haiwei官网菜单列表 * * @param HwWebMenu1 haiwei官网菜单 * @return haiwei官网菜单 */ @Override public List selectHwWebMenuList(HwWebMenu1 HwWebMenu1) { List hwWebMenus = HwWebMenuMapper1.selectHwWebMenuList(HwWebMenu1); return hwWebMenus; } /** * 获取菜单树列表 */ @Override public List selectMenuTree(HwWebMenu1 HwWebMenu1) { List hwWebMenus = HwWebMenuMapper1.selectHwWebMenuList(HwWebMenu1); return buildWebMenuTree(hwWebMenus); } /** * 新增haiwei官网菜单 * * @param HwWebMenu1 haiwei官网菜单 * @return 结果 */ @Override public int insertHwWebMenu(HwWebMenu1 HwWebMenu1) { return HwWebMenuMapper1.insertHwWebMenu(HwWebMenu1); } /** * 修改haiwei官网菜单 * * @param HwWebMenu1 haiwei官网菜单 * @return 结果 */ @Override public int updateHwWebMenu(HwWebMenu1 HwWebMenu1) { return HwWebMenuMapper1.updateHwWebMenu(HwWebMenu1); } /** * 批量删除haiwei官网菜单 * * @param webMenuIds 需要删除的haiwei官网菜单主键 * @return 结果 */ @Override public int deleteHwWebMenuByWebMenuIds(Long[] webMenuIds) { return HwWebMenuMapper1.deleteHwWebMenuByWebMenuIds(webMenuIds); } /** * 删除haiwei官网菜单信息 * * @param webMenuId haiwei官网菜单主键 * @return 结果 */ @Override public int deleteHwWebMenuByWebMenuId(Long webMenuId) { return HwWebMenuMapper1.deleteHwWebMenuByWebMenuId(webMenuId); } /** * 构建前端所需要树结构(根据传入的平铺菜单列表构造树) * * @param menus 菜单列表 * @return 树结构列表 */ public List buildWebMenuTree(List menus) { List returnList = new ArrayList<>(); List tempList = menus.stream().map(HwWebMenu1::getWebMenuId).collect(Collectors.toList()); for (HwWebMenu1 menu : menus) { // 如果是顶级节点(parent为null、0或者不在当前列表中), 遍历该父节点的所有子节点 if (menu.getParent() == null || menu.getParent() == 0L || !tempList.contains(menu.getParent())) { recursionFn(menus, menu); returnList.add(menu); } } if (returnList.isEmpty()) { returnList = menus; } return returnList; } /** * 递归设置子节点 */ private void recursionFn(List list, HwWebMenu1 t) { // 得到子节点列表 List childList = getChildList(list, t); t.setChildren(childList); for (HwWebMenu1 tChild : childList) { if (hasChild(list, tChild)) { recursionFn(list, tChild); } } } /** * 得到子节点列表 */ private List getChildList(List list, HwWebMenu1 t) { List tlist = new ArrayList(); Iterator it = list.iterator(); while (it.hasNext()) { HwWebMenu1 n = it.next(); if (StringUtils.isNotNull(n.getParent()) && n.getParent().longValue() == t.getWebMenuId().longValue()) { tlist.add(n); } } return tlist; } /** * 判断是否有子节点 */ private boolean hasChild(List list, HwWebMenu1 t) { return !getChildList(list, t).isEmpty(); } } ``` #### 6.2.5 `HwWebDocumentServiceImpl` ```java package com.ruoyi.portal.service.impl; import java.util.List; import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.common.core.utils.DateUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ruoyi.portal.mapper.HwWebDocumentMapper; import com.ruoyi.portal.domain.HwWebDocument; import com.ruoyi.portal.service.IHwWebDocumentService; /** * Hw资料文件Service业务层处理 * * @author zch * @date 2025-09-22 */ @Service public class HwWebDocumentServiceImpl implements IHwWebDocumentService { @Autowired private HwWebDocumentMapper hwWebDocumentMapper; /** * 查询Hw资料文件 * * @param documentId Hw资料文件主键 * @return Hw资料文件 */ @Override public HwWebDocument selectHwWebDocumentByDocumentId(String documentId) { return hwWebDocumentMapper.selectHwWebDocumentByDocumentId(documentId); } /** * 查询Hw资料文件列表 * * @param hwWebDocument Hw资料文件 * @return Hw资料文件 */ @Override public List selectHwWebDocumentList(HwWebDocument hwWebDocument) { return hwWebDocumentMapper.selectHwWebDocumentList(hwWebDocument); } /** * 新增Hw资料文件 * * @param hwWebDocument Hw资料文件 * @return 结果 */ @Override public int insertHwWebDocument(HwWebDocument hwWebDocument) { hwWebDocument.setCreateTime(DateUtils.getNowDate()); return hwWebDocumentMapper.insertHwWebDocument(hwWebDocument); } /** * 修改Hw资料文件 * * @param hwWebDocument Hw资料文件 * @return 结果 */ @Override public int updateHwWebDocument(HwWebDocument hwWebDocument) { // 特殊处理 secretKey:前端不传或传 null 时清空数据库密钥 // 将 null 转换为空字符串,触发 Mapper 更新条件 if (hwWebDocument.getSecretKey() == null) { hwWebDocument.setSecretKey(""); } return hwWebDocumentMapper.updateHwWebDocument(hwWebDocument); } /** * 批量删除Hw资料文件 * * @param documentIds 需要删除的Hw资料文件主键 * @return 结果 */ @Override public int deleteHwWebDocumentByDocumentIds(String[] documentIds) { return hwWebDocumentMapper.deleteHwWebDocumentByDocumentIds(documentIds); } /** * 删除Hw资料文件信息 * * @param documentId Hw资料文件主键 * @return 结果 */ @Override public int deleteHwWebDocumentByDocumentId(String documentId) { return hwWebDocumentMapper.deleteHwWebDocumentByDocumentId(documentId); } @Override public String verifyAndGetDocumentAddress(String documentId, String providedKey) throws Exception { HwWebDocument document = selectHwWebDocumentByDocumentId(documentId); if (document == null) { throw new ServiceException("文件不存在"); } String secretKey = document.getSecretKey(); String address = document.getDocumentAddress(); // 若数据库密钥为空,则直接返回文件地址 if (secretKey == null || secretKey.trim().isEmpty()) { return address; } // 若密钥不为空,则需要验证提供的密钥是否相等 String trimmedProvided = providedKey == null ? null : providedKey.trim(); if (trimmedProvided == null || trimmedProvided.isEmpty()) { throw new ServiceException("密钥不能为空"); } if (secretKey.trim().equals(trimmedProvided)) { return address; } else { throw new ServiceException("密钥错误"); } } ``` ### 6.5 Mapper XML 源码 > 本节保留 5 个模块对应的 MyBatis Mapper XML 全量源码,便于直接查看 SQL、结果映射与软删除实现细节。 #### 6.5.1 `HwWebMapper.xml` ```xml select web_id, web_json, web_json_string, web_code, web_json_english, is_delete from hw_web insert into hw_web web_json, web_json_string, web_code, web_json_english, is_delete, #{webJson}, #{webJsonString}, #{webCode}, #{webJsonEnglish}, #{isDelete}, '0', update hw_web web_json = #{webJson}, web_json_string = #{webJsonString}, web_json_english = #{webJsonEnglish}, where web_code = #{webCode} update hw_web set is_delete = '1' where web_id = #{webId} update hw_web set is_delete = '1' where web_id in #{webId} ``` #### 6.5.2 `HwWebMapper1.xml` ```xml select web_id, web_json, web_json_string, web_code, device_id, typeId, web_json_english, is_delete from hw_web1 insert into hw_web1 web_json, web_json_string, web_code, device_id, typeId, web_json_english, is_delete, #{webJson}, #{webJsonString}, #{webCode}, #{deviceId}, #{typeId}, #{webJsonEnglish}, #{isDelete}, '0', update hw_web1 web_json = #{webJson}, web_json_string = #{webJsonString}, web_json_english = #{webJsonEnglish}, where web_code = #{webCode} and device_id = #{deviceId} and typeId = #{typeId} update hw_web1 set is_delete = '1' where web_id = #{webId} update hw_web1 set is_delete = '1' where web_id in #{webId} ``` #### 6.5.3 `HwWebDocumentMapper.xml` ```xml select document_id, tenant_id, document_address, create_time, web_code, secretKey , json, type, is_delete from hw_web_document insert into hw_web_document document_id, tenant_id, document_address, create_time, web_code, secretKey, json, type, is_delete, #{documentId}, #{tenantId}, #{documentAddress}, #{createTime}, #{webCode}, #{secretKey}, #{json}, #{type}, #{isDelete}, '0', update hw_web_document document_id = #{documentId}, tenant_id = #{tenantId}, document_address = #{documentAddress}, create_time = #{createTime}, web_code = #{webCode}, secretKey = NULL #{secretKey} , json = #{json}, type = #{type}, where document_id = #{documentId} update hw_web_document set is_delete = '1' where document_id = #{documentId} update hw_web_document set is_delete = '1' where document_id in #{documentId} ``` #### 6.5.4 `HwWebMenuMapper.xml` ```xml select web_menu_id, parent, ancestors, status, web_menu_name, tenant_id, web_menu__pic, web_menu_type, web_menu_name_english, is_delete from hw_web_menu insert into hw_web_menu web_menu_id, parent, ancestors, status, web_menu_name, tenant_id, web_menu__pic, web_menu_type, web_menu_name_english, is_delete, #{webMenuId}, #{parent}, #{ancestors}, #{status}, #{webMenuName}, #{tenantId}, #{webMenuPic}, #{webMenuType}, #{webMenuNameEnglish}, #{isDelete}, '0', update hw_web_menu parent = #{parent}, ancestors = #{ancestors}, status = #{status}, web_menu_name = #{webMenuName}, tenant_id = #{tenantId}, web_menu__pic = #{webMenuPic}, web_menu_type = #{webMenuType}, web_menu_name_english = #{webMenuNameEnglish}, where web_menu_id = #{webMenuId} update hw_web_menu set is_delete = '1' where web_menu_id = #{webMenuId} update hw_web_menu set is_delete = '1' where web_menu_id in #{webMenuId} ``` #### 6.5.5 `HwWebMenuMapper1.xml` ```xml select web_menu_id, parent, ancestors, status, web_menu_name, tenant_id, web_menu__pic, value, web_menu_type, web_menu_name_english, is_delete from hw_web_menu1 insert into hw_web_menu1 web_menu_id, parent, ancestors, status, web_menu_name, tenant_id, web_menu__pic, web_menu_type, value, web_menu_name_english, is_delete, #{webMenuId}, #{parent}, #{ancestors}, #{status}, #{webMenuName}, #{tenantId}, #{webMenuPic}, #{webMenuType}, #{value}, #{webMenuNameEnglish}, #{isDelete}, '0', update hw_web_menu1 parent = #{parent}, ancestors = #{ancestors}, status = #{status}, web_menu_name = #{webMenuName}, tenant_id = #{tenantId}, web_menu__pic = #{webMenuPic}, web_menu_type = #{webMenuType}, value = #{value}, web_menu_name_english = #{webMenuNameEnglish}, where web_menu_id = #{webMenuId} update hw_web_menu1 set is_delete = '1' where web_menu_id = #{webMenuId} update hw_web_menu1 set is_delete = '1' where web_menu_id in #{webMenuId}