From 660b1e91a9fcf1a00b5b54891c25fbd5860ef10f Mon Sep 17 00:00:00 2001 From: xuyuxiang Date: Tue, 31 Jan 2023 11:05:08 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E6=9B=B4=E6=96=B0=E3=80=91=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E7=A7=BB=E5=8A=A8=E7=AB=AF=E8=8F=9C=E5=8D=95,menu?= =?UTF-8?q?=E7=9A=84pages=E6=94=B9=E4=B8=BApath=EF=BC=8C=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=BA=86menuType=E5=AD=97=E6=AE=B5=EF=BC=8C=E4=B8=8E=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E4=B8=80=E8=87=B4=EF=BC=8C=E5=89=8D=E7=AB=AF=E8=AE=B0?= =?UTF-8?q?=E5=BE=97=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/vip/xiaonuo/mobile/README.md | 1 + .../MobileMenuApi.java} | 32 +- .../vip/xiaonuo/sys/api/SysRelationApi.java | 8 + .../service/impl/GenBasicServiceImpl.java | 4 +- snowy-plugin/snowy-plugin-mobile/pom.xml | 2 +- .../controller/MobileMenuController.java | 15 + .../modular/resource/entity/MobileMenu.java | 28 +- .../enums/MobileResourceMenuStatusEnum.java | 45 +++ .../enums/MobileResourceMenuTypeEnum.java | 52 +++ .../param/menu/MobileMenuAddParam.java | 30 +- .../menu/MobileMenuChangeModuleParam.java | 40 ++ .../param/menu/MobileMenuEditParam.java | 20 +- .../provider/MobileMenuApiProvider.java | 45 +++ .../resource/service/MobileMenuService.java | 43 +++ .../service/impl/MobileMenuServiceImpl.java | 346 +++++++++++++++++- .../service/impl/MobileModuleServiceImpl.java | 25 +- snowy-plugin/snowy-plugin-sys/pom.xml | 7 + .../service/impl/SysIndexServiceImpl.java | 1 - .../enums/SysRelationCategoryEnum.java | 2 +- .../provider/SysRelationApiProvider.java | 8 + .../role/controller/SysRoleController.java | 25 +- .../SysRoleGrantMobileMenuTreeResult.java | 31 +- .../modular/role/service/SysRoleService.java | 14 +- .../role/service/impl/SysRoleServiceImpl.java | 31 +- .../user/service/impl/SysUserServiceImpl.java | 14 +- .../xiaonuo/core/config/GlobalConfigure.java | 2 + .../main/resources/_sql/update20230131.sql | 3 +- 27 files changed, 767 insertions(+), 107 deletions(-) create mode 100644 snowy-plugin-api/snowy-plugin-mobile-api/src/main/java/vip/xiaonuo/mobile/README.md rename snowy-plugin-api/snowy-plugin-mobile-api/src/main/java/vip/xiaonuo/mobile/{package-info.java => api/MobileMenuApi.java} (56%) create mode 100644 snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/enums/MobileResourceMenuStatusEnum.java create mode 100644 snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/enums/MobileResourceMenuTypeEnum.java create mode 100644 snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/param/menu/MobileMenuChangeModuleParam.java create mode 100644 snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/provider/MobileMenuApiProvider.java diff --git a/snowy-plugin-api/snowy-plugin-mobile-api/src/main/java/vip/xiaonuo/mobile/README.md b/snowy-plugin-api/snowy-plugin-mobile-api/src/main/java/vip/xiaonuo/mobile/README.md new file mode 100644 index 00000000..26849bea --- /dev/null +++ b/snowy-plugin-api/snowy-plugin-mobile-api/src/main/java/vip/xiaonuo/mobile/README.md @@ -0,0 +1 @@ +#移动端API接口 \ No newline at end of file diff --git a/snowy-plugin-api/snowy-plugin-mobile-api/src/main/java/vip/xiaonuo/mobile/package-info.java b/snowy-plugin-api/snowy-plugin-mobile-api/src/main/java/vip/xiaonuo/mobile/api/MobileMenuApi.java similarity index 56% rename from snowy-plugin-api/snowy-plugin-mobile-api/src/main/java/vip/xiaonuo/mobile/package-info.java rename to snowy-plugin-api/snowy-plugin-mobile-api/src/main/java/vip/xiaonuo/mobile/api/MobileMenuApi.java index ba79db40..1636a883 100644 --- a/snowy-plugin-api/snowy-plugin-mobile-api/src/main/java/vip/xiaonuo/mobile/package-info.java +++ b/snowy-plugin-api/snowy-plugin-mobile-api/src/main/java/vip/xiaonuo/mobile/api/MobileMenuApi.java @@ -10,4 +10,34 @@ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip */ -package vip.xiaonuo.biz; \ No newline at end of file +package vip.xiaonuo.mobile.api; + +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.json.JSONObject; + +import java.util.List; + +/** + * 移动端菜单API + * + * @author xuyuxiang + * @date 2023/1/31 10:09 + **/ +public interface MobileMenuApi { + + /** + * 获取移动端菜单授权树 + * + * @author xuyuxiang + * @date 2023/1/31 10:10 + **/ + List mobileMenuTreeSelector(); + + /** + * 获取移动端登录菜单树 + * + * @author xuyuxiang + * @date 2023/1/31 10:29 + **/ + List> loginMobileMenuTree(List menuIdList); +} diff --git a/snowy-plugin-api/snowy-plugin-sys-api/src/main/java/vip/xiaonuo/sys/api/SysRelationApi.java b/snowy-plugin-api/snowy-plugin-sys-api/src/main/java/vip/xiaonuo/sys/api/SysRelationApi.java index 1df91d96..b424d22f 100644 --- a/snowy-plugin-api/snowy-plugin-sys-api/src/main/java/vip/xiaonuo/sys/api/SysRelationApi.java +++ b/snowy-plugin-api/snowy-plugin-sys-api/src/main/java/vip/xiaonuo/sys/api/SysRelationApi.java @@ -29,4 +29,12 @@ public interface SysRelationApi { * @date 2022/6/6 11:43 **/ List getUserIdListByRoleIdList(List roleIdList); + + /** + * 根据移动端菜单Id集合移除角色和移动端菜单关系 + * + * @author xuyuxiang + * @date 2023/1/31 9:54 + **/ + void removeRoleHasMobileMenuRelationByMenuIdList(List targetIdList); } diff --git a/snowy-plugin/snowy-plugin-gen/src/main/java/vip/xiaonuo/gen/modular/basic/service/impl/GenBasicServiceImpl.java b/snowy-plugin/snowy-plugin-gen/src/main/java/vip/xiaonuo/gen/modular/basic/service/impl/GenBasicServiceImpl.java index 13229b36..5061e6e9 100644 --- a/snowy-plugin/snowy-plugin-gen/src/main/java/vip/xiaonuo/gen/modular/basic/service/impl/GenBasicServiceImpl.java +++ b/snowy-plugin/snowy-plugin-gen/src/main/java/vip/xiaonuo/gen/modular/basic/service/impl/GenBasicServiceImpl.java @@ -85,13 +85,13 @@ public class GenBasicServiceImpl extends ServiceImpl i private static final String DB_PASSWORD_KEY = "spring.datasource.dynamic.datasource.master.password"; - private static final String MODULE_KEY = "mobile"; + private static final String MODULE_KEY = "biz"; private static final String GEN_PROJECT_FRONT_PLUGIN_KEY = "snowy-admin-web"; private static final String GEN_PROJECT_PLUGIN_KEY = "snowy-plugin"; - private static final String GEN_PROJECT_PLUGIN_BIZ_KEY = GEN_PROJECT_PLUGIN_KEY + File.separator + "snowy-plugin-mobile"; + private static final String GEN_PROJECT_PLUGIN_BIZ_KEY = GEN_PROJECT_PLUGIN_KEY + File.separator + "snowy-plugin-biz"; private static final List GEN_SQL_FILE_LIST = CollectionUtil.newArrayList( JSONUtil.createObj().set("name", "Mysql.sql.btl"), diff --git a/snowy-plugin/snowy-plugin-mobile/pom.xml b/snowy-plugin/snowy-plugin-mobile/pom.xml index 61a10cf8..93d2df8b 100644 --- a/snowy-plugin/snowy-plugin-mobile/pom.xml +++ b/snowy-plugin/snowy-plugin-mobile/pom.xml @@ -29,7 +29,7 @@ ${project.parent.version} - + vip.xiaonuo snowy-plugin-sys-api diff --git a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/controller/MobileMenuController.java b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/controller/MobileMenuController.java index 1dae031a..b106bf76 100644 --- a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/controller/MobileMenuController.java +++ b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/controller/MobileMenuController.java @@ -93,6 +93,21 @@ public class MobileMenuController { return CommonResult.ok(); } + /** + * 更改移动端菜单所属模块 + * + * @author xuyuxiang + * @date 2022/4/24 20:47 + */ + @ApiOperationSupport(order = 5) + @ApiOperation("更改移动端菜单所属模块") + @CommonLog("更改移动端菜单所属模块") + @PostMapping("/mobile/menu/changeModule") + public CommonResult changeModule(@RequestBody @Valid MobileMenuChangeModuleParam mobileMenuChangeModuleParam) { + mobileMenuService.changeModule(mobileMenuChangeModuleParam); + return CommonResult.ok(); + } + /** * 删除移动端菜单 * diff --git a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/entity/MobileMenu.java b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/entity/MobileMenu.java index 813ec4ee..3b51bae5 100644 --- a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/entity/MobileMenu.java +++ b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/entity/MobileMenu.java @@ -49,40 +49,44 @@ public class MobileMenu extends CommonEntity { @ApiModelProperty(value = "编码", position = 4) private String code; - /** 路径 */ - @ApiModelProperty(value = "路径", position = 5) - private String pages; - /** 分类 */ - @ApiModelProperty(value = "分类", position = 6) + @ApiModelProperty(value = "分类", position = 5) private String category; /** 模块 */ - @ApiModelProperty(value = "模块", position = 7) + @ApiModelProperty(value = "模块", position = 6) private String module; + /** 菜单类型 */ + @ApiModelProperty(value = "菜单类型", position = 7) + private String menuType; + + /** 路径 */ + @ApiModelProperty(value = "路径", position = 8) + private String path; + /** 图标 */ - @ApiModelProperty(value = "图标", position = 8) + @ApiModelProperty(value = "图标", position = 9) private String icon; /** 颜色 */ - @ApiModelProperty(value = "颜色", position = 9) + @ApiModelProperty(value = "颜色", position = 10) private String color; /** 规则类型 */ - @ApiModelProperty(value = "规则类型", position = 10) + @ApiModelProperty(value = "规则类型", position = 11) private String regType; /** 可用状态 */ - @ApiModelProperty(value = "可用状态", position = 11) + @ApiModelProperty(value = "可用状态", position = 12) private String status; /** 排序码 */ - @ApiModelProperty(value = "排序码", position = 12) + @ApiModelProperty(value = "排序码", position = 13) private Integer sortCode; /** 扩展信息 */ - @ApiModelProperty(value = "扩展信息", position = 13) + @ApiModelProperty(value = "扩展信息", position = 14) @TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED) private String extJson; } diff --git a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/enums/MobileResourceMenuStatusEnum.java b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/enums/MobileResourceMenuStatusEnum.java new file mode 100644 index 00000000..377e3d14 --- /dev/null +++ b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/enums/MobileResourceMenuStatusEnum.java @@ -0,0 +1,45 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package vip.xiaonuo.mobile.modular.resource.enums; + +import lombok.Getter; +import vip.xiaonuo.common.exception.CommonException; + +/** + * 移动端资菜单状态枚举 + * + * @author xuyuxiang + * @date 2022/4/21 19:56 + **/ +@Getter +public enum MobileResourceMenuStatusEnum { + + /** 可用 */ + ENABLE("ENABLE"), + + /** 不可用 */ + DISABLED("DISABLED"); + + private final String value; + + MobileResourceMenuStatusEnum(String value) { + this.value = value; + } + + public static void validate(String value) { + boolean flag = ENABLE.getValue().equals(value) || DISABLED.getValue().equals(value); + if(!flag) { + throw new CommonException("不支持的资源分类:{}", value); + } + } +} diff --git a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/enums/MobileResourceMenuTypeEnum.java b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/enums/MobileResourceMenuTypeEnum.java new file mode 100644 index 00000000..6099461f --- /dev/null +++ b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/enums/MobileResourceMenuTypeEnum.java @@ -0,0 +1,52 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package vip.xiaonuo.mobile.modular.resource.enums; + +import lombok.Getter; +import vip.xiaonuo.common.exception.CommonException; + +/** + * 移动端菜单类型枚举 + * + * @author xuyuxiang + * @date 2022/4/21 19:56 + **/ +@Getter +public enum MobileResourceMenuTypeEnum { + + /** 目录 */ + CATALOG("CATALOG"), + + /** 组件 */ + MENU("MENU"), + + /** 内链 */ + IFRAME("IFRAME"), + + /** 外链 */ + LINK("LINK"); + + private final String value; + + MobileResourceMenuTypeEnum(String value) { + this.value = value; + } + + public static void validate(String value) { + boolean flag = CATALOG.getValue().equals(value) || MENU.getValue().equals(value) || IFRAME.getValue().equals(value) || + LINK.getValue().equals(value); + if(!flag) { + throw new CommonException("不支持的菜单类型:{}", value); + } + } +} diff --git a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/param/menu/MobileMenuAddParam.java b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/param/menu/MobileMenuAddParam.java index accfcd2d..3b6792ae 100644 --- a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/param/menu/MobileMenuAddParam.java +++ b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/param/menu/MobileMenuAddParam.java @@ -38,43 +38,47 @@ public class MobileMenuAddParam { @NotBlank(message = "title不能为空") private String title; - /** 界面路径 */ - @ApiModelProperty(value = "界面路径", required = true, position = 3) - @NotBlank(message = "pages不能为空") - private String pages; - /** 分类 */ - @ApiModelProperty(value = "分类", required = true, position = 4) + @ApiModelProperty(value = "分类", required = true, position = 3) @NotBlank(message = "category不能为空") private String category; /** 模块 */ - @ApiModelProperty(value = "模块", required = true, position = 5) + @ApiModelProperty(value = "模块", required = true, position = 4) @NotBlank(message = "module不能为空") private String module; + /** 菜单类型 */ + @ApiModelProperty(value = "菜单类型", position = 5) + @NotBlank(message = "menuType不能为空") + private String menuType; + + /** 路径 */ + @ApiModelProperty(value = "路径", position = 6) + @NotBlank(message = "path不能为空") + private String path; + /** 图标 */ - @ApiModelProperty(value = "图标", required = true, position = 6) + @ApiModelProperty(value = "图标", required = true, position = 7) @NotBlank(message = "icon不能为空") private String icon; /** 颜色 */ - @ApiModelProperty(value = "颜色", required = true, position = 7) + @ApiModelProperty(value = "颜色", required = true, position = 8) @NotBlank(message = "color不能为空") private String color; /** 规则类型 */ - @ApiModelProperty(value = "规则类型", required = true, position = 8) + @ApiModelProperty(value = "规则类型", required = true, position = 9) @NotBlank(message = "regType不能为空") private String regType; /** 可用状态 */ - @ApiModelProperty(value = "可用状态", required = true, position = 9) + @ApiModelProperty(value = "可用状态", required = true, position = 10) @NotBlank(message = "status不能为空") private String status; /** 排序码 */ - @ApiModelProperty(value = "排序码", position = 10) + @ApiModelProperty(value = "排序码", position = 11) private Integer sortCode; - } diff --git a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/param/menu/MobileMenuChangeModuleParam.java b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/param/menu/MobileMenuChangeModuleParam.java new file mode 100644 index 00000000..7b16755d --- /dev/null +++ b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/param/menu/MobileMenuChangeModuleParam.java @@ -0,0 +1,40 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package vip.xiaonuo.mobile.modular.resource.param.menu; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; + +/** + * 移动端菜单更改所属模块参数 + * + * @author xuyuxiang + * @date 2022/7/27 18:40 + **/ +@Getter +@Setter +public class MobileMenuChangeModuleParam { + + /** id */ + @ApiModelProperty(value = "id", required = true) + @NotBlank(message = "id不能为空") + private String id; + + /** 模块 */ + @ApiModelProperty(value = "module", required = true) + @NotBlank(message = "module不能为空") + private String module; +} diff --git a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/param/menu/MobileMenuEditParam.java b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/param/menu/MobileMenuEditParam.java index 1bbf1c93..d7f0d7eb 100644 --- a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/param/menu/MobileMenuEditParam.java +++ b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/param/menu/MobileMenuEditParam.java @@ -43,21 +43,26 @@ public class MobileMenuEditParam { @NotBlank(message = "title不能为空") private String title; - /** 界面路径 */ - @ApiModelProperty(value = "界面路径", required = true, position = 5) - @NotBlank(message = "pages不能为空") - private String pages; - /** 分类 */ - @ApiModelProperty(value = "分类", required = true, position = 6) + @ApiModelProperty(value = "分类", required = true, position = 4) @NotBlank(message = "category不能为空") private String category; /** 模块 */ - @ApiModelProperty(value = "模块", required = true, position = 7) + @ApiModelProperty(value = "模块", required = true, position = 5) @NotBlank(message = "module不能为空") private String module; + /** 菜单类型 */ + @ApiModelProperty(value = "菜单类型", position = 6) + @NotBlank(message = "menuType不能为空") + private String menuType; + + /** 路径 */ + @ApiModelProperty(value = "路径", position = 7) + @NotBlank(message = "path不能为空") + private String path; + /** 图标 */ @ApiModelProperty(value = "图标", required = true, position = 8) @NotBlank(message = "icon不能为空") @@ -81,5 +86,4 @@ public class MobileMenuEditParam { /** 排序码 */ @ApiModelProperty(value = "排序码", position = 12) private Integer sortCode; - } diff --git a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/provider/MobileMenuApiProvider.java b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/provider/MobileMenuApiProvider.java new file mode 100644 index 00000000..0255c37a --- /dev/null +++ b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/provider/MobileMenuApiProvider.java @@ -0,0 +1,45 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package vip.xiaonuo.mobile.modular.resource.provider; + +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.json.JSONObject; +import org.springframework.stereotype.Service; +import vip.xiaonuo.mobile.api.MobileMenuApi; +import vip.xiaonuo.mobile.modular.resource.service.MobileMenuService; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 移动端菜单API接口提供者 + * + * @author xuyuxiang + * @date 2023/1/31 10:12 + **/ +@Service +public class MobileMenuApiProvider implements MobileMenuApi { + + @Resource + private MobileMenuService mobileMenuService; + + @Override + public List mobileMenuTreeSelector() { + return mobileMenuService.mobileMenuTreeSelector(); + } + + @Override + public List> loginMobileMenuTree(List menuIdList) { + return mobileMenuService.loginMobileMenuTree(menuIdList); + } +} diff --git a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/service/MobileMenuService.java b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/service/MobileMenuService.java index 6ab4da69..6edd3e94 100644 --- a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/service/MobileMenuService.java +++ b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/service/MobileMenuService.java @@ -13,6 +13,7 @@ package vip.xiaonuo.mobile.modular.resource.service; import cn.hutool.core.lang.tree.Tree; +import cn.hutool.json.JSONObject; import com.baomidou.mybatisplus.extension.service.IService; import vip.xiaonuo.mobile.modular.resource.entity.MobileMenu; import vip.xiaonuo.mobile.modular.resource.entity.MobileModule; @@ -52,6 +53,14 @@ public interface MobileMenuService extends IService { */ void edit(MobileMenuEditParam mobileMenuEditParam); + /** + * 更改移动端菜单所属模块 + * + * @author xuyuxiang + * @date 2022/9/26 13:09 + **/ + void changeModule(MobileMenuChangeModuleParam mobileMenuChangeModuleParam); + /** * 删除移动端菜单 * @@ -76,6 +85,24 @@ public interface MobileMenuService extends IService { **/ MobileMenu queryEntity(String id); + /* ====以下为各种递归方法==== */ + + /** + * 根据id获取所有的子数据列表 + * + * @author xuyuxiang + * @date 2022/8/2 14:52 + */ + List getChildListById(List originDataList, String id, boolean includeSelf); + + /** + * 根据id获取所有的父数据列表 + * + * @author xuyuxiang + * @date 2022/8/2 14:52 + */ + List getParentListById(List originDataList, String id, boolean includeSelf); + /** * 获取模块选择器 * @@ -91,4 +118,20 @@ public interface MobileMenuService extends IService { * @date 2023/01/28 22:42 **/ List> menuTreeSelector(MobileMenuSelectorMenuParam mobileMenuSelectorMenuParam); + + /** + * 获取移动端菜单授权树 + * + * @author xuyuxiang + * @date 2023/1/31 10:14 + **/ + List mobileMenuTreeSelector(); + + /** + * 获取移动端登录菜单树 + * + * @author xuyuxiang + * @date 2023/1/31 10:30 + **/ + List> loginMobileMenuTree(List menuIdList); } diff --git a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/service/impl/MobileMenuServiceImpl.java b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/service/impl/MobileMenuServiceImpl.java index 7a7cad00..ed228f74 100644 --- a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/service/impl/MobileMenuServiceImpl.java +++ b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/service/impl/MobileMenuServiceImpl.java @@ -14,10 +14,14 @@ package vip.xiaonuo.mobile.modular.resource.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollStreamUtil; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.lang.tree.TreeNode; import cn.hutool.core.lang.tree.TreeUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -31,9 +35,13 @@ import vip.xiaonuo.mobile.modular.resource.mapper.MobileMenuMapper; import vip.xiaonuo.mobile.modular.resource.param.menu.*; import vip.xiaonuo.mobile.modular.resource.service.MobileMenuService; import vip.xiaonuo.mobile.modular.resource.service.MobileModuleService; +import vip.xiaonuo.sys.api.SysRelationApi; import javax.annotation.Resource; +import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; /** @@ -48,6 +56,9 @@ public class MobileMenuServiceImpl extends ServiceImpl> tree(MobileMenuTreeParam mobileMenuTreeParam) { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); @@ -70,6 +81,23 @@ public class MobileMenuServiceImpl extends ServiceImpl().eq(MobileMenu::getParentId, mobileMenu.getParentId()) + .eq(MobileMenu::getCategory, MobileResourceCategoryEnum.MENU.getValue()).eq(MobileMenu::getTitle, mobileMenu.getTitle())) > 0; + if(repeatTitle) { + throw new CommonException("存在重复的菜单,名称为:{}", mobileMenu.getTitle()); + } + List originDataList = this.list(new LambdaQueryWrapper().eq(MobileMenu::getCategory, + MobileResourceCategoryEnum.MENU.getValue())); + if(!mobileMenuAddParam.getParentId().equals("0")) { + MobileMenu parentMenu = this.getById(originDataList, mobileMenuAddParam.getParentId()); + if(ObjectUtil.isEmpty(parentMenu)) { + throw new CommonException("上级菜单不存在,id值为:{}", mobileMenuAddParam.getParentId()); + } + if(!parentMenu.getModule().equals(mobileMenuAddParam.getModule())) { + throw new CommonException("module与上级菜单不一致"); + } + } + mobileMenu.setCategory(MobileResourceCategoryEnum.MENU.getValue()); this.save(mobileMenu); } @@ -77,14 +105,62 @@ public class MobileMenuServiceImpl extends ServiceImpl().eq(MobileMenu::getParentId, mobileMenu.getParentId()) + .eq(MobileMenu::getCategory, MobileResourceCategoryEnum.MENU.getValue()).eq(MobileMenu::getTitle, mobileMenu.getTitle()) + .ne(MobileMenu::getId, mobileMenu.getId())) > 0; + if(repeatTitle) { + throw new CommonException("存在重复的菜单,名称为:{}", mobileMenu.getTitle()); + } + List originDataList = this.list(new LambdaQueryWrapper().eq(MobileMenu::getCategory, + MobileResourceCategoryEnum.MENU.getValue())); + boolean errorLevel = this.getChildListById(originDataList, mobileMenu.getId(), true).stream() + .map(MobileMenu::getId).collect(Collectors.toList()).contains(mobileMenu.getParentId()); + if(errorLevel) { + throw new CommonException("不可选择上级菜单:{}", this.getById(originDataList, mobileMenu.getParentId()).getTitle()); + } + if(!mobileMenuEditParam.getParentId().equals("0")) { + MobileMenu parentMenu = this.getById(originDataList, mobileMenuEditParam.getParentId()); + if(ObjectUtil.isEmpty(parentMenu)) { + throw new CommonException("上级菜单不存在,id值为:{}", mobileMenuEditParam.getParentId()); + } + if(!parentMenu.getModule().equals(mobileMenuEditParam.getModule())) { + throw new CommonException("module与上级菜单不一致"); + } + } this.updateById(mobileMenu); } + @Override + public void changeModule(MobileMenuChangeModuleParam mobileMenuChangeModuleParam) { + MobileMenu mobileMenu = this.queryEntity(mobileMenuChangeModuleParam.getId()); + if(!mobileMenu.getParentId().equals("0")) { + throw new CommonException("非顶级菜单不可修改所属模块"); + } + List mobileMenuList = this.list(new LambdaQueryWrapper().eq(MobileMenu::getCategory, + MobileResourceCategoryEnum.MENU.getValue())); + List mobileMenuChildList = this.getChildListById(mobileMenuList, mobileMenu.getId(), true).stream() + .peek(mobileMenuTemp -> mobileMenuTemp.setModule(mobileMenuChangeModuleParam.getModule())).collect(Collectors.toList()); + this.updateBatchById(mobileMenuChildList); + } + @Transactional(rollbackFor = Exception.class) @Override public void delete(List mobileMenuIdParamList) { - // 执行删除 - this.removeBatchByIds(CollStreamUtil.toList(mobileMenuIdParamList, MobileMenuIdParam::getId)); + List mobileMenuIdList = CollStreamUtil.toList(mobileMenuIdParamList, MobileMenuIdParam::getId); + if(ObjectUtil.isNotEmpty(mobileMenuIdList)) { + // 获取菜单下的菜单 + List allMenuList = this.list(new LambdaQueryWrapper() + .in(MobileMenu::getCategory, CollectionUtil.newArrayList(MobileResourceCategoryEnum.MENU.getValue()))); + List toDeleteMenuIdList = CollectionUtil.newArrayList(); + mobileMenuIdList.forEach(menuId -> toDeleteMenuIdList.addAll(this.getChildListById(allMenuList, menuId, true).stream() + .map(MobileMenu::getId).collect(Collectors.toList()))); + if(ObjectUtil.isNotEmpty(toDeleteMenuIdList)) { + // 清除对应的角色与移动端资源信息 + sysRelationApi.removeRoleHasMobileMenuRelationByMenuIdList(toDeleteMenuIdList); + // 执行删除 + this.removeBatchByIds(toDeleteMenuIdList); + } + } } @Override @@ -120,10 +196,270 @@ public class MobileMenuServiceImpl extends ServiceImpl resourceList = this.list(lambdaQueryWrapper); - List> treeNodeList = resourceList.stream().map(sysMenu -> - new TreeNode<>(sysMenu.getId(), sysMenu.getParentId(), - sysMenu.getTitle(), sysMenu.getSortCode()).setExtra(JSONUtil.parseObj(sysMenu))) + List> treeNodeList = resourceList.stream().map(mobileMenu -> + new TreeNode<>(mobileMenu.getId(), mobileMenu.getParentId(), + mobileMenu.getTitle(), mobileMenu.getSortCode()).setExtra(JSONUtil.parseObj(mobileMenu))) .collect(Collectors.toList()); return TreeUtil.build(treeNodeList, "0"); } + + @Override + public List mobileMenuTreeSelector() { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.in(MobileMenu::getCategory, MobileResourceCategoryEnum.MODULE.getValue(), MobileResourceCategoryEnum.MENU.getValue()); + List allModuleAndMenuList = this.list(lambdaQueryWrapper); + List mobileModuleList = CollectionUtil.newArrayList(); + List mobileMenuList = CollectionUtil.newArrayList(); + allModuleAndMenuList.forEach(mobileMenu -> { + if (mobileMenu.getCategory().equals(MobileResourceCategoryEnum.MODULE.getValue())) mobileModuleList.add(mobileMenu); + if (mobileMenu.getCategory().equals(MobileResourceCategoryEnum.MENU.getValue())) mobileMenuList.add(mobileMenu); + }); + List leafMenuList = CollectionUtil.newArrayList(); + MobileMenu rootMobileMenu = new MobileMenu(); + rootMobileMenu.setId("0"); + rootMobileMenu.setParentId("-1"); + rootMobileMenu.setSortCode(-1); + mobileMenuList.add(rootMobileMenu); + List> treeNodeList = mobileMenuList.stream().map(mobileMenu -> + new TreeNode<>(mobileMenu.getId(), mobileMenu.getParentId(), + mobileMenu.getTitle(), mobileMenu.getSortCode())).collect(Collectors.toList()); + List> treeList = TreeUtil.build(treeNodeList, "-1"); + mobileMenuList.forEach(mobileMenu -> { + boolean isLeafMenu = this.getChildListById(mobileMenuList, mobileMenu.getId(), false).size() == 0; + if(isLeafMenu) { + JSONObject mobileRoleGrantResourceMenuResult = JSONUtil.createObj(); + BeanUtil.copyProperties(mobileMenu, mobileRoleGrantResourceMenuResult); + JSONObject parentJsonObject = getParentNode(treeList, mobileMenu); + List parentIdSplitList = StrUtil.split(parentJsonObject.getStr("parentId"), StrUtil.DASHED); + List parentNameSplitList = StrUtil.split(parentJsonObject.getStr("parentName"), StrUtil.DASHED); + if(parentNameSplitList.size() > 1) { + mobileRoleGrantResourceMenuResult.set("parentId", parentIdSplitList.get(3)); + mobileRoleGrantResourceMenuResult.set("parentName", parentNameSplitList.get(0)); + StringBuilder selfNamePrefix = new StringBuilder(); + for(int i = 1; i< parentNameSplitList.size(); i++) { + selfNamePrefix.append(parentNameSplitList.get(i)).append(StrUtil.DASHED); + } + mobileRoleGrantResourceMenuResult.set("title", selfNamePrefix + mobileRoleGrantResourceMenuResult.getStr("title")); + } else { + mobileRoleGrantResourceMenuResult.set("parentName", parentJsonObject.getStr("parentName")); + } + leafMenuList.add(mobileRoleGrantResourceMenuResult); + } + }); + Map> menuListGroup = leafMenuList.stream() + .collect(Collectors.groupingBy(jsonObject -> jsonObject.getStr("module"))); + return mobileModuleList.stream().map(mobileModule -> { + JSONObject mobileRoleGrantResourceTreeResult = JSONUtil.createObj(); + mobileRoleGrantResourceTreeResult.set("id", mobileModule.getId()); + mobileRoleGrantResourceTreeResult.set("title", mobileModule.getTitle()); + mobileRoleGrantResourceTreeResult.set("icon", mobileModule.getIcon()); + mobileRoleGrantResourceTreeResult.set("menu", menuListGroup.get(mobileModule.getId())); + return mobileRoleGrantResourceTreeResult; + }).collect(Collectors.toList()); + } + + @Override + public List> loginMobileMenuTree(List menuIdList) { + // 获取所有的菜单和模块以及单页面列表,并按分类和排序码排序 + List allModuleAndMenuAndSpaList = this.list(new LambdaQueryWrapper() + .in(MobileMenu::getCategory, MobileResourceCategoryEnum.MODULE.getValue(), MobileResourceCategoryEnum.MENU.getValue()) + .orderByAsc(CollectionUtil.newArrayList(MobileMenu::getCategory, MobileMenu::getSortCode))); + // 全部以菜单承载 + List allModuleList = CollectionUtil.newArrayList(); + List allMenuList = CollectionUtil.newArrayList(); + // 根据类型抽取 + allModuleAndMenuAndSpaList.forEach(mobileMenu -> { + boolean isModule = mobileMenu.getCategory().equals(MobileResourceCategoryEnum.MODULE.getValue()); + if (isModule) { + // 抽取所有的模块列表 + allModuleList.add(mobileMenu); + } + boolean isMenu = mobileMenu.getCategory().equals(MobileResourceCategoryEnum.MENU.getValue()); + if (isMenu) { + // 抽取所有的菜单列表 + allMenuList.add(mobileMenu); + } + }); + + // 定义结果 + List resultList = CollectionUtil.newArrayList(); + + // 获取拥有的菜单列表 + List menuList = allMenuList.stream().filter(mobileMenu -> + menuIdList.contains(mobileMenu.getId())).collect(Collectors.toList()); + + // 对获取到的角色对应的菜单列表进行处理,获取父列表 + menuList.forEach(mobileMenu -> execRecursionFindParent(allMenuList, mobileMenu.getId(), resultList)); + + // 将拥有的菜单列表添加 + resultList.addAll(menuList); + + // 获取模块id集合 + Set moduleIdSet = resultList.stream().map(MobileMenu::getModule).collect(Collectors.toSet()); + + // 抽取拥有的模块列表 + List moduleList = allModuleList.stream().filter(mobileMenu -> + moduleIdSet.contains(mobileMenu.getId())).collect(Collectors.toList()); + + // 如果一个模块都没拥有 + if (ObjectUtil.isEmpty(moduleList)) { + // 返回空列表 + return CollectionUtil.newArrayList(); + } + + // 将拥有的模块放入集合 + resultList.addAll(moduleList); + + // 最终处理,构造meta + List resultJsonObjectList = resultList.stream().map(mobileMenu -> { + + // 将模块的父id设置为0,设置随机pages + if (mobileMenu.getCategory().equals(MobileResourceCategoryEnum.MODULE.getValue())) { + mobileMenu.setParentId("0"); + mobileMenu.setPath(StrUtil.SLASH + RandomUtil.randomString(10)); + } + // 将根菜单的父id设置为模块的id + if (mobileMenu.getCategory().equals(MobileResourceCategoryEnum.MENU.getValue())) { + if (mobileMenu.getParentId().equals("0")) { + mobileMenu.setParentId(mobileMenu.getModule()); + } + } + JSONObject menuJsonObject = JSONUtil.parseObj(mobileMenu); + JSONObject metaJsonObject = JSONUtil.createObj(); + metaJsonObject.set("icon", mobileMenu.getIcon()); + metaJsonObject.set("title", mobileMenu.getTitle()); + metaJsonObject.set("type", mobileMenu.getCategory().toLowerCase()); + // 如果是菜单,则设置type菜单类型为小写 + if (mobileMenu.getCategory().equals(MobileResourceCategoryEnum.MENU.getValue())) { + metaJsonObject.set("type", mobileMenu.getMenuType().toLowerCase()); + } + menuJsonObject.set("meta", metaJsonObject); + return menuJsonObject; + }).collect(Collectors.toList()); + + // 执行构造树 + List> treeNodeList = resultJsonObjectList.stream().map(jsonObject -> + new TreeNode<>(jsonObject.getStr("id"), jsonObject.getStr("parentId"), + jsonObject.getStr("title"), jsonObject.getInt("sortCode")).setExtra(JSONUtil.parseObj(jsonObject))) + .collect(Collectors.toList()); + return TreeUtil.build(treeNodeList, "0"); + } + + /* ====以下为各种递归方法==== */ + + public JSONObject getParentNode(List> treeList, MobileMenu mobileMenu) { + List> resultList = CollectionUtil.newArrayList(); + getNode(treeList, mobileMenu.getId(), resultList); + JSONObject jsonObject = JSONUtil.createObj(); + if(ObjectUtil.isNotEmpty(resultList)) { + Tree currentNode = resultList.get(0); + if(currentNode.getId().equals("0") || currentNode.getParentId().equals("0")) { + jsonObject.set("parentId", mobileMenu.getId()); + jsonObject.set("parentName", mobileMenu.getTitle()); + } else { + jsonObject.set("parentId", StrUtil.join(StrUtil.DASHED, CollectionUtil.reverse(CollectionUtil + .removeNull(this.getParentsId(currentNode, false))))); + jsonObject.set("parentName", StrUtil.join(StrUtil.DASHED, CollectionUtil.reverse(CollectionUtil + .removeNull(TreeUtil.getParentsName(currentNode, false))))); + } + } else { + jsonObject.set("parentId", mobileMenu.getId()); + jsonObject.set("parentName", mobileMenu.getTitle()); + } + return jsonObject; + } + + public List getParentsId(Tree node, boolean includeCurrentNode) { + final List result = new ArrayList<>(); + if (null == node) { + return result; + } + + if (includeCurrentNode) { + result.add(node.getId()); + } + + Tree parent = node.getParent(); + while (null != parent) { + result.add(parent.getId()); + parent = parent.getParent(); + } + return result; + } + + public void getNode(List> treeList, String id, List> resultList) { + for (Tree tree: treeList) { + if(tree.getId().equals(id)) { + resultList.add(tree); + break; + } else { + List> children = tree.getChildren(); + if(ObjectUtil.isNotEmpty(children)) { + getNode(children, id, resultList); + } + } + } + } + + @Override + public List getChildListById(List originDataList, String id, boolean includeSelf) { + List resultList = CollectionUtil.newArrayList(); + execRecursionFindChild(originDataList, id, resultList); + if(includeSelf) { + MobileMenu self = this.getById(originDataList, id); + if(ObjectUtil.isNotEmpty(self)) { + resultList.add(self); + } + } + return resultList; + } + + @Override + public List getParentListById(List originDataList, String id, boolean includeSelf) { + List resultList = CollectionUtil.newArrayList(); + execRecursionFindParent(originDataList, id, resultList); + if(includeSelf) { + MobileMenu self = this.getById(originDataList, id); + if(ObjectUtil.isNotEmpty(self)) { + resultList.add(self); + } + } + return resultList; + } + + public void execRecursionFindChild(List originDataList, String id, List resultList) { + originDataList.forEach(item -> { + if(item.getParentId().equals(id)) { + resultList.add(item); + execRecursionFindChild(originDataList, item.getId(), resultList); + } + }); + } + + public void execRecursionFindParent(List originDataList, String id, List resultList) { + originDataList.forEach(item -> { + if(item.getId().equals(id)) { + MobileMenu parent = this.getById(originDataList, item.getParentId()); + if(ObjectUtil.isNotEmpty(parent)) { + resultList.add(parent); + } + execRecursionFindParent(originDataList, item.getParentId(), resultList); + } + }); + } + + public MobileMenu getById(List originDataList, String id) { + int index = CollStreamUtil.toList(originDataList, MobileMenu::getId).indexOf(id); + return index == -1?null:originDataList.get(index); + } + + public MobileMenu getParentById(List originDataList, String id) { + MobileMenu self = this.getById(originDataList, id); + return ObjectUtil.isNotEmpty(self)?self:this.getById(originDataList, self.getParentId()); + } + + public MobileMenu getChildById(List originDataList, String id) { + int index = CollStreamUtil.toList(originDataList, MobileMenu::getParentId).indexOf(id); + return index == -1?null:originDataList.get(index); + } } diff --git a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/service/impl/MobileModuleServiceImpl.java b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/service/impl/MobileModuleServiceImpl.java index 272dae55..a4b51d8e 100644 --- a/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/service/impl/MobileModuleServiceImpl.java +++ b/snowy-plugin/snowy-plugin-mobile/src/main/java/vip/xiaonuo/mobile/modular/resource/service/impl/MobileModuleServiceImpl.java @@ -13,6 +13,8 @@ package vip.xiaonuo.mobile.modular.resource.service.impl; import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollStreamUtil; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; @@ -24,6 +26,7 @@ import org.springframework.stereotype.Service; import vip.xiaonuo.common.enums.CommonSortOrderEnum; import vip.xiaonuo.common.exception.CommonException; import vip.xiaonuo.common.page.CommonPageRequest; +import vip.xiaonuo.mobile.modular.resource.entity.MobileMenu; import vip.xiaonuo.mobile.modular.resource.entity.MobileModule; import vip.xiaonuo.mobile.modular.resource.enums.MobileResourceCategoryEnum; import vip.xiaonuo.mobile.modular.resource.mapper.MobileModuleMapper; @@ -33,9 +36,11 @@ import vip.xiaonuo.mobile.modular.resource.param.module.MobileModuleIdParam; import vip.xiaonuo.mobile.modular.resource.param.module.MobileModulePageParam; import vip.xiaonuo.mobile.modular.resource.service.MobileMenuService; import vip.xiaonuo.mobile.modular.resource.service.MobileModuleService; +import vip.xiaonuo.sys.api.SysRelationApi; import javax.annotation.Resource; import java.util.List; +import java.util.stream.Collectors; /** * 模块Service接口实现类 @@ -49,10 +54,8 @@ public class MobileModuleServiceImpl extends ServiceImpl page(MobileModulePageParam mobileModulePageParam) { @@ -99,16 +102,10 @@ public class MobileModuleServiceImpl extends ServiceImpl mobileModuleIdParamList) { - /*List mobileModuleIdList = CollStreamUtil.toList(mobileModuleIdParamList, MobileModuleIdParam::getId); + List mobileModuleIdList = CollStreamUtil.toList(mobileModuleIdParamList, MobileModuleIdParam::getId); if(ObjectUtil.isNotEmpty(mobileModuleIdList)) { - boolean containsMobiletemModule = this.listByIds(mobileModuleIdList).stream().map(MobileModule::getCode) - .collect(Collectors.toSet()).contains(MobileBuildInEnum.BUILD_IN_MODULE_CODE.getValue()); - if(containsMobiletemModule) { - throw new CommonException("不可删除系统内置模块"); - } - // 获取模块下的菜单 - List allMenuList = mobileMenuService.list(new LambdaUpdateWrapper() + List allMenuList = mobileMenuService.list(new LambdaQueryWrapper() .in(MobileMenu::getCategory, CollectionUtil.newArrayList(MobileResourceCategoryEnum.MENU.getValue()))); if(ObjectUtil.isNotEmpty(allMenuList)) { List toDeleteMenuIdList = CollectionUtil.newArrayList(mobileModuleIdList); @@ -118,14 +115,12 @@ public class MobileModuleServiceImpl extends ServiceImpl().in(MobileRelation::getTargetId, toDeleteMenuIdList) - .eq(MobileRelation::getCategory, MobileRelationCategoryEnum.MOBILE_ROLE_HAS_RESOURCE.getValue())); + sysRelationApi.removeRoleHasMobileMenuRelationByMenuIdList(toDeleteMenuIdList); // 执行删除 this.removeBatchByIds(toDeleteMenuIdList); } } - }*/ - // TODO + } } @Override diff --git a/snowy-plugin/snowy-plugin-sys/pom.xml b/snowy-plugin/snowy-plugin-sys/pom.xml index 45e3a0bb..ac2d8541 100644 --- a/snowy-plugin/snowy-plugin-sys/pom.xml +++ b/snowy-plugin/snowy-plugin-sys/pom.xml @@ -35,5 +35,12 @@ snowy-plugin-dev-api ${project.parent.version} + + + + vip.xiaonuo + snowy-plugin-mobile-api + ${project.parent.version} + \ No newline at end of file diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/index/service/impl/SysIndexServiceImpl.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/index/service/impl/SysIndexServiceImpl.java index fa0b4234..8dd207e8 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/index/service/impl/SysIndexServiceImpl.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/index/service/impl/SysIndexServiceImpl.java @@ -16,7 +16,6 @@ import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.json.JSONUtil; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.springframework.stereotype.Service; import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser; import vip.xiaonuo.auth.core.util.StpLoginUserUtil; diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/relation/enums/SysRelationCategoryEnum.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/relation/enums/SysRelationCategoryEnum.java index 6d9533f4..faf3a173 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/relation/enums/SysRelationCategoryEnum.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/relation/enums/SysRelationCategoryEnum.java @@ -36,7 +36,7 @@ public enum SysRelationCategoryEnum { SYS_ROLE_HAS_RESOURCE("SYS_ROLE_HAS_RESOURCE"), /** 角色拥有移动端菜单 */ - SYS_ROLE_HAS_MENU_MOBILE("SYS_ROLE_HAS_MOBILE_MENU"), + SYS_ROLE_HAS_MOBILE_MENU("SYS_ROLE_HAS_MOBILE_MENU"), /** 角色拥有权限 */ SYS_ROLE_HAS_PERMISSION("SYS_ROLE_HAS_PERMISSION"); diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/relation/provider/SysRelationApiProvider.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/relation/provider/SysRelationApiProvider.java index e9811733..dfa2bcad 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/relation/provider/SysRelationApiProvider.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/relation/provider/SysRelationApiProvider.java @@ -12,8 +12,10 @@ */ package vip.xiaonuo.sys.modular.relation.provider; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.springframework.stereotype.Service; import vip.xiaonuo.sys.api.SysRelationApi; +import vip.xiaonuo.sys.modular.relation.entity.SysRelation; import vip.xiaonuo.sys.modular.relation.enums.SysRelationCategoryEnum; import vip.xiaonuo.sys.modular.relation.service.SysRelationService; @@ -37,4 +39,10 @@ public class SysRelationApiProvider implements SysRelationApi { return sysRelationService.getRelationObjectIdListByTargetIdListAndCategory(roleIdList, SysRelationCategoryEnum.SYS_USER_HAS_ROLE.getValue()); } + + @Override + public void removeRoleHasMobileMenuRelationByMenuIdList(List targetIdList) { + sysRelationService.remove(new LambdaQueryWrapper().in(SysRelation::getTargetId, targetIdList) + .eq(SysRelation::getCategory, SysRelationCategoryEnum.SYS_ROLE_HAS_MOBILE_MENU.getValue())); + } } diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/controller/SysRoleController.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/controller/SysRoleController.java index 5bdb8a32..bf8d3a47 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/controller/SysRoleController.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/controller/SysRoleController.java @@ -28,9 +28,7 @@ import vip.xiaonuo.common.pojo.CommonResult; import vip.xiaonuo.common.pojo.CommonValidList; import vip.xiaonuo.sys.modular.role.entity.SysRole; import vip.xiaonuo.sys.modular.role.param.*; -import vip.xiaonuo.sys.modular.role.result.SysRoleGrantResourceTreeResult; -import vip.xiaonuo.sys.modular.role.result.SysRoleOwnPermissionResult; -import vip.xiaonuo.sys.modular.role.result.SysRoleOwnResourceResult; +import vip.xiaonuo.sys.modular.role.result.*; import vip.xiaonuo.sys.modular.role.service.SysRoleService; import vip.xiaonuo.sys.modular.user.entity.SysUser; @@ -163,7 +161,7 @@ public class SysRoleController { @ApiOperationSupport(order = 8) @ApiOperation("获取角色拥有移动端菜单") @GetMapping("/sys/role/ownMobileMenu") - public CommonResult ownMobileMenu(@Valid SysRoleIdParam sysRoleIdParam) { + public CommonResult ownMobileMenu(@Valid SysRoleIdParam sysRoleIdParam) { return CommonResult.data(sysRoleService.ownMobileMenu(sysRoleIdParam)); } @@ -267,12 +265,25 @@ public class SysRoleController { } /** - * 获取权限授权树 + * 获取移动端菜单授权树 * * @author xuyuxiang * @date 2022/4/24 20:00 */ @ApiOperationSupport(order = 16) + @ApiOperation("获取移动端菜单授权树") + @GetMapping("/sys/role/mobileMenuTreeSelector") + public CommonResult> mobileMenuTreeSelector() { + return CommonResult.data(sysRoleService.mobileMenuTreeSelector()); + } + + /** + * 获取权限授权树 + * + * @author xuyuxiang + * @date 2022/4/24 20:00 + */ + @ApiOperationSupport(order = 17) @ApiOperation("获取权限授权树") @GetMapping("/sys/role/permissionTreeSelector") public CommonResult> permissionTreeSelector() { @@ -285,7 +296,7 @@ public class SysRoleController { * @author xuyuxiang * @date 2022/4/24 20:00 */ - @ApiOperationSupport(order = 17) + @ApiOperationSupport(order = 18) @ApiOperation("获取角色选择器") @GetMapping("/sys/role/roleSelector") public CommonResult> roleSelector(SysRoleSelectorRoleParam sysRoleSelectorRoleParam) { @@ -298,7 +309,7 @@ public class SysRoleController { * @author xuyuxiang * @date 2022/4/24 20:00 */ - @ApiOperationSupport(order = 18) + @ApiOperationSupport(order = 19) @ApiOperation("获取用户选择器") @GetMapping("/sys/role/userSelector") public CommonResult> userSelector(SysRoleSelectorUserParam sysRoleSelectorUserParam) { diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/result/SysRoleGrantMobileMenuTreeResult.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/result/SysRoleGrantMobileMenuTreeResult.java index 530886bf..559ed1fa 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/result/SysRoleGrantMobileMenuTreeResult.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/result/SysRoleGrantMobileMenuTreeResult.java @@ -28,12 +28,12 @@ import java.util.List; @Setter public class SysRoleGrantMobileMenuTreeResult { - /** 分类id */ - @ApiModelProperty(value = "分类id", position = 1) + /** 模块id */ + @ApiModelProperty(value = "模块id", position = 1) private String id; - /** 分类名称*/ - @ApiModelProperty(value = "分类名称", position = 2) + /** 模块名称*/ + @ApiModelProperty(value = "模块名称", position = 2) private String title; /** 模块图标 */ @@ -73,28 +73,5 @@ public class SysRoleGrantMobileMenuTreeResult { /** 模块 */ @ApiModelProperty(value = "菜单模块", position = 5) private String module; - - /** 菜单下按钮集合 */ - @ApiModelProperty(value = "菜单下按钮集合", position = 6) - private List button; - - /** - * 授权按钮类 - * - * @author xuyuxiang - * @date 2022/8/13 16:54 - */ - @Getter - @Setter - public static class SysRoleGrantResourceButtonResult { - - /** 按钮id */ - @ApiModelProperty(value = "按钮id", position = 1) - private String id; - - /** 标题 */ - @ApiModelProperty(value = "按钮标题", position = 2) - private String title; - } } } diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/service/SysRoleService.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/service/SysRoleService.java index 1312ea0d..0d7fef5d 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/service/SysRoleService.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/service/SysRoleService.java @@ -17,9 +17,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import vip.xiaonuo.sys.modular.role.entity.SysRole; import vip.xiaonuo.sys.modular.role.param.*; -import vip.xiaonuo.sys.modular.role.result.SysRoleGrantResourceTreeResult; -import vip.xiaonuo.sys.modular.role.result.SysRoleOwnPermissionResult; -import vip.xiaonuo.sys.modular.role.result.SysRoleOwnResourceResult; +import vip.xiaonuo.sys.modular.role.result.*; import vip.xiaonuo.sys.modular.user.entity.SysUser; import java.util.List; @@ -102,7 +100,7 @@ public interface SysRoleService extends IService { * @author xuyuxiang * @date 2022/5/13 20:51 */ - SysRoleOwnResourceResult ownMobileMenu(SysRoleIdParam SysRoleIdParam); + SysRoleOwnMobileMenuResult ownMobileMenu(SysRoleIdParam SysRoleIdParam); /** * 给角色授权移动端菜单 @@ -162,6 +160,14 @@ public interface SysRoleService extends IService { */ List resourceTreeSelector(); + /** + * 获取移动端菜单授权树 + * + * @author xuyuxiang + * @date 2022/4/24 20:08 + */ + List mobileMenuTreeSelector(); + /** * 获取权限授权树 * diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/service/impl/SysRoleServiceImpl.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/service/impl/SysRoleServiceImpl.java index 4b442cb2..6826cf18 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/service/impl/SysRoleServiceImpl.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/role/service/impl/SysRoleServiceImpl.java @@ -38,6 +38,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl import vip.xiaonuo.common.enums.CommonSortOrderEnum; import vip.xiaonuo.common.exception.CommonException; import vip.xiaonuo.common.page.CommonPageRequest; +import vip.xiaonuo.mobile.api.MobileMenuApi; import vip.xiaonuo.sys.core.enums.SysBuildInEnum; import vip.xiaonuo.sys.modular.org.entity.SysOrg; import vip.xiaonuo.sys.modular.org.service.SysOrgService; @@ -51,9 +52,7 @@ import vip.xiaonuo.sys.modular.role.entity.SysRole; import vip.xiaonuo.sys.modular.role.enums.SysRoleCategoryEnum; import vip.xiaonuo.sys.modular.role.mapper.SysRoleMapper; import vip.xiaonuo.sys.modular.role.param.*; -import vip.xiaonuo.sys.modular.role.result.SysRoleGrantResourceTreeResult; -import vip.xiaonuo.sys.modular.role.result.SysRoleOwnPermissionResult; -import vip.xiaonuo.sys.modular.role.result.SysRoleOwnResourceResult; +import vip.xiaonuo.sys.modular.role.result.*; import vip.xiaonuo.sys.modular.role.service.SysRoleService; import vip.xiaonuo.sys.modular.user.entity.SysUser; import vip.xiaonuo.sys.modular.user.service.SysUserService; @@ -85,6 +84,9 @@ public class SysRoleServiceImpl extends ServiceImpl impl @Resource private SysUserService sysUserService; + @Resource + private MobileMenuApi mobileMenuApi; + @Override public Page page(SysRolePageParam sysRolePageParam) { QueryWrapper queryWrapper = new QueryWrapper<>(); @@ -212,14 +214,24 @@ public class SysRoleServiceImpl extends ServiceImpl impl } @Override - public SysRoleOwnResourceResult ownMobileMenu(SysRoleIdParam SysRoleIdParam) { - // TODO - return null; + public SysRoleOwnMobileMenuResult ownMobileMenu(SysRoleIdParam sysRoleIdParam) { + SysRoleOwnMobileMenuResult sysRoleOwnMobileMenuResult = new SysRoleOwnMobileMenuResult(); + sysRoleOwnMobileMenuResult.setId(sysRoleIdParam.getId()); + sysRoleOwnMobileMenuResult.setGrantInfoList(sysRelationService.getRelationListByObjectIdAndCategory(sysRoleIdParam.getId(), + SysRelationCategoryEnum.SYS_ROLE_HAS_MOBILE_MENU.getValue()).stream().map(sysRelation -> + JSONUtil.toBean(sysRelation.getExtJson(), SysRoleOwnMobileMenuResult.SysRoleOwnMobileMenu.class)).collect(Collectors.toList())); + return sysRoleOwnMobileMenuResult; } @Override public void grantMobileMenu(SysRoleGrantMobileMenuParam sysRoleGrantMobileMenuParam) { - // TODO + String id = sysRoleGrantMobileMenuParam.getId(); + List menuIdList = sysRoleGrantMobileMenuParam.getGrantInfoList().stream() + .map(SysRoleGrantMobileMenuParam.SysRoleGrantMobileMenu::getMenuId).collect(Collectors.toList()); + List extJsonList = sysRoleGrantMobileMenuParam.getGrantInfoList().stream() + .map(JSONUtil::toJsonStr).collect(Collectors.toList()); + sysRelationService.saveRelationBatchWithClear(id, menuIdList, SysRelationCategoryEnum.SYS_ROLE_HAS_MOBILE_MENU.getValue(), + extJsonList); } @Override @@ -353,6 +365,11 @@ public class SysRoleServiceImpl extends ServiceImpl impl }).collect(Collectors.toList()); } + @Override + public List mobileMenuTreeSelector() { + return BeanUtil.copyToList(mobileMenuApi.mobileMenuTreeSelector(), SysRoleGrantMobileMenuTreeResult.class); + } + @Override public List permissionTreeSelector() { List permissionResult = CollectionUtil.newArrayList(); diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/service/impl/SysUserServiceImpl.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/service/impl/SysUserServiceImpl.java index 2fdd93b7..b7d4d558 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/service/impl/SysUserServiceImpl.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/service/impl/SysUserServiceImpl.java @@ -56,6 +56,7 @@ import vip.xiaonuo.dev.api.DevConfigApi; import vip.xiaonuo.dev.api.DevEmailApi; import vip.xiaonuo.dev.api.DevMessageApi; import vip.xiaonuo.dev.api.DevSmsApi; +import vip.xiaonuo.mobile.api.MobileMenuApi; import vip.xiaonuo.sys.core.enums.SysBuildInEnum; import vip.xiaonuo.sys.modular.org.entity.SysOrg; import vip.xiaonuo.sys.modular.org.service.SysOrgService; @@ -124,6 +125,9 @@ public class SysUserServiceImpl extends ServiceImpl impl @Resource private DevMessageApi devMessageApi; + @Resource + private MobileMenuApi mobileMenuApi; + @Resource private SysOrgService sysOrgService; @@ -679,8 +683,14 @@ public class SysUserServiceImpl extends ServiceImpl impl @Override public List> ownMobileMenu(SysUserIdParam sysUserIdParam) { - // TODO 获取移动菜单树 - return null; + // 获取角色id列表 + List roleIdList = this.ownRole(sysUserIdParam); + List> resultList = CollectionUtil.newArrayList(); + if (ObjectUtil.isNotEmpty(roleIdList)) { + resultList = mobileMenuApi.loginMobileMenuTree(sysRelationService.getRelationTargetIdListByObjectIdListAndCategory(roleIdList, + SysRelationCategoryEnum.SYS_ROLE_HAS_RESOURCE.getValue())); + } + return resultList; } /** diff --git a/snowy-web-app/src/main/java/vip/xiaonuo/core/config/GlobalConfigure.java b/snowy-web-app/src/main/java/vip/xiaonuo/core/config/GlobalConfigure.java index ec0a1cae..4a4d80ae 100644 --- a/snowy-web-app/src/main/java/vip/xiaonuo/core/config/GlobalConfigure.java +++ b/snowy-web-app/src/main/java/vip/xiaonuo/core/config/GlobalConfigure.java @@ -168,6 +168,8 @@ public class GlobalConfigure implements WebMvcConfigurer { "/dev/sms/page", "/dev/sms/delete", "/dev/sms/detail", + "/mobile/menu/**", + "/mobile/module/**", }; /** diff --git a/snowy-web-app/src/main/resources/_sql/update20230131.sql b/snowy-web-app/src/main/resources/_sql/update20230131.sql index 870b5665..7ca98fa5 100644 --- a/snowy-web-app/src/main/resources/_sql/update20230131.sql +++ b/snowy-web-app/src/main/resources/_sql/update20230131.sql @@ -10,9 +10,10 @@ CREATE TABLE `mobile_resource` ( `PARENT_ID` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '父ID', `TITLE` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称', `CODE` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '编码', - `PAGES` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '界面路径', `CATEGORY` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '分类', `MODULE` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '模块', + `MENU_TYPE` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '菜单类型', + `PATH` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '路径', `ICON` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '图标', `COLOR` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '颜色', `REG_TYPE` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '规则类型',