Kaynağa Gözat

更新 代码生成器
新增 api模块

18339543638 2 yıl önce
ebeveyn
işleme
1ede0cac2e
27 değiştirilmiş dosya ile 640 ekleme ve 60 silme
  1. 6 0
      pom.xml
  2. 7 0
      tr-dependencies/pom.xml
  3. 21 0
      tr-framework/src/main/java/cn/tr/core/utils/TreeUtil.java
  4. 19 0
      tr-modules-api/pom.xml
  5. 21 0
      tr-modules-api/tr-module-system-api/pom.xml
  6. 24 0
      tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysMenuApi.java
  7. 5 0
      tr-modules/tr-module-gen/pom.xml
  8. 1 1
      tr-modules/tr-module-gen/src/main/java/cn/tr/module/gen/modular/basic/param/GenBasicAddParam.java
  9. 4 0
      tr-modules/tr-module-gen/src/main/java/cn/tr/module/gen/modular/basic/param/GenBasicEditParam.java
  10. 18 16
      tr-modules/tr-module-gen/src/main/java/cn/tr/module/gen/modular/basic/service/impl/GenBasicServiceImpl.java
  11. 5 4
      tr-modules/tr-module-gen/src/main/resources/backend/Controller.java.btl
  12. 1 1
      tr-modules/tr-module-gen/src/main/resources/backend/DTO.java.btl
  13. 1 1
      tr-modules/tr-module-gen/src/main/resources/backend/Service.java.btl
  14. 1 1
      tr-modules/tr-module-gen/src/main/resources/frontend/Api.ts.btl
  15. 101 1
      tr-modules/tr-module-gen/src/main/resources/frontend/data.ts.btl
  16. 1 1
      tr-modules/tr-module-gen/src/main/resources/frontend/formDrawer.vue.btl
  17. 196 0
      tr-modules/tr-module-gen/src/main/resources/frontend/index.vue.btl
  18. 3 3
      tr-modules/tr-module-gen/src/main/resources/frontend/viewDrawer.vue.btl
  19. 2 3
      tr-modules/tr-module-system/pom.xml
  20. 2 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/mapper/user/SysMenuMapper.java
  21. 1 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/service/CurrentUserService.java
  22. 3 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/SysMenuQueryDTO.java
  23. 122 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/provider/SysMenuApiProvider.java
  24. 18 12
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/ISysMenuService.java
  25. 46 16
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/impl/SysMenuServiceImpl.java
  26. 10 0
      tr-plugins/tr-spring-boot-starter-plugin-mybatis/src/main/java/cn/tr/plugin/mybatis/service/ITreeService.java
  27. 1 0
      tr-test/src/main/resources/application-doc.yml

+ 6 - 0
pom.xml

@@ -11,10 +11,16 @@
     <description>驼人通用型管理框架</description>
 
     <modules>
+        <!--依赖包-->
         <module>tr-dependencies</module>
+        <!--插件-->
         <module>tr-plugins</module>
+        <!--一些框架公共服务-->
         <module>tr-framework</module>
+        <!--系统、业务服务模块-->
         <module>tr-modules</module>
+        <!--系统、业务服务对外提供api模块-->
+        <module>tr-modules-api</module>
         <module>tr-test</module>
     </modules>
 

+ 7 - 0
tr-dependencies/pom.xml

@@ -424,6 +424,13 @@
                 <version>${revision}</version>
             </dependency>
 
+            <!--api模块-->
+            <dependency>
+                <groupId>cn.tr</groupId>
+                <artifactId>tr-module-system-api</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
             <!--阿里云短信-->
             <dependency>
                 <groupId>com.aliyun</groupId>

+ 21 - 0
tr-framework/src/main/java/cn/tr/core/utils/TreeUtil.java

@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.tr.core.tree.TreeNode;
+import javafx.util.Pair;
 import lombok.experimental.UtilityClass;
 
 import java.util.*;
@@ -100,4 +101,24 @@ public class TreeUtil {
         });
         CollectionUtil.sort(children,Comparator.comparing(TreeNode::getSort));
     }
+
+    public static <T extends TreeNode> List<T> flatTree(Collection<T> sources) {
+        List<T> result = new ArrayList<>();
+        for (T source : sources) {
+            flatTree(source,result);
+        }
+        return result;
+    }
+
+    private static  <T extends TreeNode>  void flatTree(T source,Collection<T> result){
+        if(source==null){
+            return;
+        }
+        result.add(source);
+        if(CollectionUtil.isNotEmpty(source.getChildren())){
+            for (Object child : source.getChildren()) {
+                flatTree((T) child,result);
+            }
+        }
+    }
 }

+ 19 - 0
tr-modules-api/pom.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>tr-footstone</artifactId>
+        <groupId>cn.tr</groupId>
+        <version>0.0.9</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>tr-modules-api</artifactId>
+    <packaging>pom</packaging>
+    <modules>
+        <module>tr-module-system-api</module>
+    </modules>
+
+
+</project>

+ 21 - 0
tr-modules-api/tr-module-system-api/pom.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>tr-modules-api</artifactId>
+        <groupId>cn.tr</groupId>
+        <version>0.0.9</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>tr-module-system-api</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-framework</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 24 - 0
tr-modules-api/tr-module-system-api/src/main/java/cn/tr/module/api/sys/user/SysMenuApi.java

@@ -0,0 +1,24 @@
+package cn.tr.module.api.sys.user;
+
+
+import cn.hutool.core.lang.Pair;
+
+/**
+ * @Interface : SysMenuApi
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月14日
+ */
+
+public interface SysMenuApi {
+    /**
+     * 代码生成菜单插入
+     * @param parentId          父级菜单id
+     * @param busName           业务名称
+     * @param frontModuleName   前端模块名称
+     * @param functionName      功能名称
+     * @param permissionPrefix  权限前缀
+     * @return <menuId,componentPath> <菜单id,组件路径>
+     */
+    Pair<String,String> addForGenMenu(String parentId, String busName, String frontModuleName,String backModuleName,String permissionPrefix, String functionName);
+}

+ 5 - 0
tr-modules/tr-module-gen/pom.xml

@@ -40,5 +40,10 @@
             <groupId>com.ibeetl</groupId>
             <artifactId>beetl-framework-starter</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-module-system-api</artifactId>
+        </dependency>
     </dependencies>
 </project>

+ 1 - 1
tr-modules/tr-module-gen/src/main/java/cn/tr/module/gen/modular/basic/param/GenBasicAddParam.java

@@ -57,7 +57,7 @@ public class GenBasicAddParam {
 
     /** 上级目录 */
     @ApiModelProperty(value = "上级目录", required = true, position = 6)
-//    @NotNull(message = "menuPid不能为空")
+    @NotNull(message = "menuPid不能为空")
     private String menuPid;
 
     /** 功能名 */

+ 4 - 0
tr-modules/tr-module-gen/src/main/java/cn/tr/module/gen/modular/basic/param/GenBasicEditParam.java

@@ -38,6 +38,10 @@ public class GenBasicEditParam {
     @NotNull(message = "dbTable不能为空")
     private String dbTable;
 
+    @ApiModelProperty(value = "上级目录", required = true, position = 6)
+    @NotNull(message = "menuPid不能为空")
+    private String menuPid;
+
     /** 主表主键 */
     @ApiModelProperty(value = "主表主键", position = 3)
     @NotNull(message = "dbTableKey不能为空")

+ 18 - 16
tr-modules/tr-module-gen/src/main/java/cn/tr/module/gen/modular/basic/service/impl/GenBasicServiceImpl.java

@@ -18,6 +18,7 @@ import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.lang.Pair;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.RandomUtil;
 import cn.hutool.core.util.StrUtil;
@@ -26,6 +27,7 @@ import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import cn.tr.core.exception.ServiceException;
 import cn.tr.core.exception.TRExcCode;
+import cn.tr.module.api.sys.user.SysMenuApi;
 import cn.tr.module.gen.core.util.CommonDownloadUtil;
 import cn.tr.module.gen.core.util.CommonResponseUtil;
 import cn.tr.module.gen.core.util.GenDbTypeUtil;
@@ -53,6 +55,7 @@ import org.beetl.core.Configuration;
 import org.beetl.core.GroupTemplate;
 import org.beetl.core.Template;
 import org.beetl.core.resource.ClasspathResourceLoader;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.env.Environment;
 import org.springframework.jdbc.support.JdbcUtils;
 import org.springframework.stereotype.Service;
@@ -74,7 +77,8 @@ import java.util.stream.Collectors;
  **/
 @Service
 public class GenBasicServiceImpl extends ServiceImpl<GenBasicMapper, GenBasic> implements GenBasicService {
-
+    @Autowired
+    private SysMenuApi sysMenuApi;
     //    private static final String DB_URL_KEY = "spring.datasource.dynamic.datasource.master.url";
     private static final String DB_URL_KEY = "spring.datasource.url";
 
@@ -97,7 +101,7 @@ public class GenBasicServiceImpl extends ServiceImpl<GenBasicMapper, GenBasic> i
             JSONUtil.createObj().set("name", "Oracle.sql.btl"));
 
     private static final List<JSONObject> GEN_FRONT_FILE_LIST = CollectionUtil.newArrayList(
-            JSONUtil.createObj().set("name", "Api.js.btl").set("path", "api" + File.separator ),
+            JSONUtil.createObj().set("name", "Api.ts.btl").set("path", "api" + File.separator ),
             JSONUtil.createObj().set("name", "data.ts.btl").set("path",  "views" + File.separator ),
             JSONUtil.createObj().set("name", "index.vue.btl").set("path",  "views" + File.separator ),
             JSONUtil.createObj().set("name", "formDrawer.vue.btl").set("path",  "views" + File.separator),
@@ -296,6 +300,8 @@ public class GenBasicServiceImpl extends ServiceImpl<GenBasicMapper, GenBasic> i
             CommonResponseUtil.renderError(response, "代码生成基础不存在,id值为:" + genBasicIdParam.getId());
             return;
         }
+        GenBasic genBasic = this.queryEntity(genBasicIdParam.getId());
+        Pair<String, String> idAndComponentId = sysMenuApi.addForGenMenu(genBasic.getMenuPid(),genBasic.getBusName(), genBasic.getFrontModuleName(), genBasic.getBackendModuleName(),genBasic.getBackendModuleName()+":"+genBasic.getBusName()+":", genBasic.getFunctionName());
         // 压缩
         File zip = ZipUtil.zip(tempFolder);
         // 压缩完毕删除临时目录
@@ -325,15 +331,8 @@ public class GenBasicServiceImpl extends ServiceImpl<GenBasicMapper, GenBasic> i
         try {
             GenBasic genBasic = this.queryEntity(genBasicIdParam.getId());
 
-//            // 生成菜单
-//            String menuId = sysMenuApi.addForGenMenu(genBasic.getMenuPid(), genBasic.getBusName(), genBasic.getModule(), genBasic.getFunctionName(),
-//                    StrUtil.SLASH + MODULE_KEY + StrUtil.SLASH + genBasic.getBusName());
-//
-//            // 生成按钮
-//            sysButtonApi.addForGenButton(menuId, genBasic.getClassName(), genBasic.getFunctionName());
-//
-//            // 授权菜单
-//            sysRoleApi.grantForGenMenuAndButton(menuId);
+            // 生成菜单
+            Pair<String, String> idAndComponentId = sysMenuApi.addForGenMenu(genBasic.getMenuPid(), genBasic.getBusName(),genBasic.getFrontModuleName(), genBasic.getBackendModuleName(), genBasic.getFunctionName(),genBasic.getBackendModuleName()+":"+genBasic.getBusName()+":");
 
             //前端代码移动到前端
             FileUtil.moveContent(FileUtil.file(tempFolder + File.separator + "frontend"), FileUtil.file(genProjectFrontendPath), true);
@@ -348,6 +347,7 @@ public class GenBasicServiceImpl extends ServiceImpl<GenBasicMapper, GenBasic> i
         }
     }
 
+
     /**
      * 获取临时目录
      *
@@ -377,11 +377,11 @@ public class GenBasicServiceImpl extends ServiceImpl<GenBasicMapper, GenBasic> i
         // 生成前端代码到临时目录
         genBasicPreviewResult.getGenBasicCodeFrontendResultList().forEach(genBasicCodeResult ->
                 FileUtil.writeUtf8String(genBasicCodeResult.getCodeFileContent(), FileUtil.file(tempFolder + File.separator
-                        + "frontend" + File.separator + genBasicCodeResult.getCodeFileWithPathName())));
+                        + "frontend" + File.separator + File.separator+genBasicCodeResult.getCodeFileWithPathName())));
         // 生成后端代码到临时目录
         genBasicPreviewResult.getGenBasicCodeBackendResultList().forEach(genBasicCodeResult ->
                 FileUtil.writeUtf8String(genBasicCodeResult.getCodeFileContent(), FileUtil.file(tempFolder + File.separator
-                        + "backend" + File.separator + genBasicCodeResult.getCodeFileWithPathName())));
+                        + "backend" + File.separator +genBasicCodeResult.getCodeFileWithPathName())));
         return tempFolder;
     }
 
@@ -423,13 +423,13 @@ public class GenBasicServiceImpl extends ServiceImpl<GenBasicMapper, GenBasic> i
                 Template templateFront = groupTemplateFront.getTemplate(fileTemplateName);
                 templateFront.binding(bindingJsonObject);
                 String resultName = StrUtil.removeSuffix(fileTemplateName, ".btl");
-                if(fileTemplateName.equalsIgnoreCase("Api.js.btl")) {
+                if(fileTemplateName.equalsIgnoreCase("Api.ts.btl")) {
                     resultName =genBasic.getFrontModuleName()+ StrUtil.upperFirst(genBasic.getBusName())+ resultName;
                     genBasicCodeFrontResult.setCodeFileName(resultName);
-                    genBasicCodeFrontResult.setCodeFileWithPathName(genFrontBasicPath + fileTemplatePath + File.separator + resultName);
+                    genBasicCodeFrontResult.setCodeFileWithPathName(genFrontBasicPath + File.separator +fileTemplatePath +File.separator+genBasic.getFrontModuleName()+ File.separator + resultName);
                 } else {
                     genBasicCodeFrontResult.setCodeFileName(resultName);
-                    genBasicCodeFrontResult.setCodeFileWithPathName(genFrontBasicPath + fileTemplatePath + File.separator + genBasic.getBusName() + File.separator + resultName);
+                    genBasicCodeFrontResult.setCodeFileWithPathName(genFrontBasicPath + File.separator + fileTemplatePath+File.separator+genBasic.getFrontModuleName() + File.separator + genBasic.getBusName() + File.separator + resultName);
                 }
                 genBasicCodeFrontResult.setCodeFileContent(templateFront.render());
                 genBasicCodeFrontendResultList.add(genBasicCodeFrontResult);
@@ -594,6 +594,8 @@ public class GenBasicServiceImpl extends ServiceImpl<GenBasicMapper, GenBasic> i
                     configItem.set("whetherRetract", genConfig.getWhetherRetract().equalsIgnoreCase(GenYesNoEnum.Y.getValue()));
                     // 增改
                     configItem.set("whetherAddUpdate", genConfig.getWhetherAddUpdate().equalsIgnoreCase(GenYesNoEnum.Y.getValue()));
+                    // 是否必填
+                    configItem.set("whetherRequired", genConfig.getWhetherRequired().equalsIgnoreCase(GenYesNoEnum.Y.getValue()));
                     // 作用类型
                     configItem.set("effectType", genConfig.getEffectType());
                     // 字典值

+ 5 - 4
tr-modules/tr-module-gen/src/main/resources/backend/Controller.java.btl

@@ -26,7 +26,8 @@ import org.springframework.web.bind.annotation.*;
  * @date ${genTime}
  */
 @Api(tags = "${functionName}控制器")
-@RestController("/${backendModuleName}/${busName}")
+@RestController
+@RequestMapping("/${backendModuleName}/${busName}")
 @AllArgsConstructor
 public class ${className}Controller extends BaseController{
 
@@ -34,9 +35,9 @@ public class ${className}Controller extends BaseController{
 
     @ApiOperationSupport(author = "${authorName}",order = 1)
     @ApiOperation(value="根据条件查询${functionName}",notes = "权限: ${backendModuleName}:${busName}:query")
-    @GetMapping("/query/page")
+    @PostMapping("/query/page")
     @SaCheckPermission("${backendModuleName}:${busName}:query")
-    public CommonResult<List<${className}DTO>> selectList(${className}QueryDTO query) {
+    public CommonResult<List<${className}DTO>> selectList(@RequestBody ${className}QueryDTO query) {
         startPage();
         return CommonResult.success(${classNameFirstLower}Service.select${className}List(query));
     }
@@ -70,6 +71,6 @@ public class ${className}Controller extends BaseController{
     @PostMapping("/removeByIds")
     @SaCheckPermission("${backendModuleName}:${busName}:remove")
     public CommonResult<Integer> delete(@RequestBody Collection<String> ids) {
-        return CommonResult.success(${classNameFirstLower}Service.remove{className}ByIds(ids));
+        return CommonResult.success(${classNameFirstLower}Service.remove${className}ByIds(ids));
     }
 }

+ 1 - 1
tr-modules/tr-module-gen/src/main/resources/backend/DTO.java.btl

@@ -26,7 +26,7 @@ public class ${className}DTO extends BaseDTO  {
     <% var fieldNameCamelCase=configList[i].fieldNameCamelCase;
 if(fieldNameCamelCase== "createTime" || fieldNameCamelCase == "createBy" || fieldNameCamelCase == "updateTime" ||fieldNameCamelCase == "updateBy" || fieldNameCamelCase == "tenantId"  || fieldNameCamelCase== "deleted" ) {
         continue ;} %>
-    <% if(!configList[i].needPage) { %>
+    <% if(configList[i].needPage) { %>
     @ApiModelProperty(value = "${configList[i].fieldRemark}", position = ${i + 1})
     <% if(configList[i].needTableId ) { %>
     @NotNull(message = "主键不能为空",groups = {Update.class})

+ 1 - 1
tr-modules/tr-module-gen/src/main/resources/backend/Service.java.btl

@@ -50,5 +50,5 @@ public interface ${className}Service{
      * @author ${authorName}
      * @date   ${genTime}
      */
-    int remove{className}ByIds(Collection<String> ids);
+    int remove${className}ByIds(Collection<String> ids);
 }

+ 1 - 1
tr-modules/tr-module-gen/src/main/resources/frontend/Api.js.btl → tr-modules/tr-module-gen/src/main/resources/frontend/Api.ts.btl

@@ -86,7 +86,7 @@ return defHttp.post({ url: Api.${frontModuleName}${busNameFirstUpper}Add, params
 *       0 编辑失败
 *       1 编辑成功
 */
-export const /${backendModuleName}/${busName}/edit = (params?: object) => {
+export const ${frontModuleName}${busNameFirstUpper}Edit = (params?: object) => {
 return defHttp.post({ url: Api.${frontModuleName}${busNameFirstUpper}Edit, params: params });
 };
 

+ 101 - 1
tr-modules/tr-module-gen/src/main/resources/frontend/data.ts.btl

@@ -112,7 +112,107 @@ export const searchFormSchema: FormSchema[] = [
 <% }  %>
 <% }  %>
 ];
-
+// 表单新增编辑
+export const dataFormSchema: FormSchema[] = [
+<% for(var i = 0; i < configList.~size; i++) { %>
+<% if(configList[i].fieldNameCamelCase != "createBy" &&configList[i].fieldNameCamelCase != "updateBy" && configList[i].fieldNameCamelCase != "tenantId"  && configList[i].fieldNameCamelCase!= "deleted"  && configList[i].whetherAddUpdate) {%>
+    {
+    label: '${configList[i].fieldRemark}',
+    field: '${configList[i].fieldNameCamelCase}',
+    <% if(configList[i].effectType == "input"){  %>
+    required: true,
+    <% }  %>
+    <% if(configList[i].effectType == "input"){  %>
+    component: 'Input',
+    componentProps: {
+    placeholder: '请输入${configList[i].fieldRemark}',
+    }
+    <% }  %>
+    <% if(configList[i].effectType == "inputTextArea"){  %>
+    component: 'InputTextArea',
+    componentProps: {
+    placeholder: '请输入${configList[i].fieldRemark}',
+    }
+    <% }  %>
+    <% if(configList[i].effectType == "inputNumber"){  %>
+    component: 'InputNumber',
+    componentProps: {
+    placeholder: '请输入${configList[i].fieldRemark}',
+    }
+    <% }  %>
+    <% if(configList[i].effectType == "select"){  %>
+    component: 'Select',
+    componentProps: {
+    options: [{ label: '', value: '' }],
+    },
+    <% }  %>
+    <% if(configList[i].effectType == "apiSelect"){  %>
+    component: 'ApiSelect',
+    componentProps: {
+    api: listDictModel,
+    params: {
+    dictCode: '${configList[i].dictTypeCode}',
+    },
+    }
+    <% }  %>
+    <% if(configList[i].effectType == "switch"){  %>
+    component: 'Switch',
+    componentProps: {
+    options: radioBoolean,
+    }
+    <% }  %>
+    <% if(configList[i].effectType == "radioGroup"){  %>
+    component: 'RadioGroup',
+    required: true,
+    componentProps: {
+    options: radioBoolean,
+    }
+    <% }  %>
+    <% if(configList[i].effectType == "apiRadioGroup"){  %>
+    component: 'ApiRadioGroup',
+    componentProps: {
+    api: listDictModel,
+    params: {
+    dictCode: '${configList[i].dictTypeCode}',
+    }
+    }
+    <% }  %>
+    <% if(configList[i].effectType == "checkboxGroup"){  %>
+    component: 'CheckboxGroup',
+    componentProps: {
+    options: [{label: '选项1',value: '1',}]
+    }
+    <% }  %>
+    <% if(configList[i].effectType == "datePicker"){  %>
+    component: 'DatePicker',
+    componentProps: {
+    placeholder: '请选择时间',
+    format: 'YYYY-MM-DD HH:mm:ss',
+    showTime: { format: 'YYYY-MM-DD HH:mm:ss' },
+    }
+    <% }  %>
+    <% if(configList[i].effectType == "upload"){  %>
+    component: 'Input',
+    slot: 'upload',
+    componentProps: {
+    placeholder: '请上传图片',
+    },
+    <% }  %>
+    <% if(configList[i].effectType == "textEditor"){  %>
+    component: 'textEditor',
+    componentProps: ({ formModel }) => {
+    return {
+    height: 300,
+    onChange: (e) => {
+    formModel.${configList[i].fieldNameCamelCase} = e;
+    },
+    };
+    }
+<% }  %>
+},
+<% }  %>
+<% }  %>
+];
 // 表单详情查看
 export const viewSchema: DescItem[] = [
 <% for(var i = 0; i < configList.~size; i++) { %>

+ 1 - 1
tr-modules/tr-module-gen/src/main/resources/frontend/formDrawer.vue.btl

@@ -18,7 +18,7 @@
     import { useMessage } from '/@/hooks/web/useMessage';
     import { dataFormSchema } from './data';
 
-    import { ${frontModuleName}${busNameFirstUpper}dd, ${frontModuleName}${busNameFirstUpper}Edit, ${frontModuleName}${busNameFirstUpper}Detail } from '/@/api/${frontModuleName}/${frontModuleName}${busNameFirstUpper}Api';
+    import { ${frontModuleName}${busNameFirstUpper}Add, ${frontModuleName}${busNameFirstUpper}Edit, ${frontModuleName}${busNameFirstUpper}Detail } from '/@/api/${frontModuleName}/${frontModuleName}${busNameFirstUpper}Api';
 
     const emit = defineEmits(['success', 'register']);
 

+ 196 - 0
tr-modules/tr-module-gen/src/main/resources/frontend/index.vue.btl

@@ -0,0 +1,196 @@
+<template>
+    <div>
+        <BasicTable @register="registerTable">
+            <template #bodyCell="{ column, record }">
+                <% for(var i = 0; i < configList.~size; i++) { %>
+                <% if(configList[i].dictTypeCode!=null) {%>
+                <template v-if="column.key === '${configList[i].fieldNameCamelCase}'">
+                    <Tag :color="formatDictColor(${configList[i].fieldNameCamelCase}Options, record.${configList[i].fieldNameCamelCase})">
+                        {{ formatDictValue(${configList[i].fieldNameCamelCase}Options, record.${configList[i].fieldNameCamelCase}) }}
+                    </Tag>
+                </template>
+                <% } %>
+                <% } %>
+                <template v-if="column.key === 'action'">
+                    <TableAction
+                            :actions="[
+              {
+                auth: '${backendModuleName}:${busName}:query',
+                icon: 'icon-eye|iconfont',
+                tooltip: '查看',
+                label: '查看',
+                onClick: handleView.bind(null, record),
+              },
+              {
+                auth: '${backendModuleName}:${busName}:edit',
+                icon: 'icon-edit|iconfont',
+                tooltip: '编辑',
+                label: '编辑',
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                auth: '${backendModuleName}:${busName}:remove',
+                icon: 'icon-delete|iconfont',
+                tooltip: '删除',
+                label: '删除',
+                color: 'error',
+                popConfirm: {
+                  title: '是否确认删除',
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record),
+                }
+              },
+            ]"
+                    />
+                </template>
+            </template>
+            <template #toolbar>
+                <Button v-auth="['${backendModuleName}:${busName}:add']" type="primary" @click="handleCreate" preIcon="icon-plus|iconfont"> 新增 </Button>
+                <Button v-auth="['${backendModuleName}:${busName}:remove']" type="primary" danger @click="handleDelete(null)" preIcon="icon-delete|iconfont">
+                    批量删除
+                </Button>
+            </template>
+        </BasicTable>
+        <FormDrawer @register="registerDrawer" @success="handleSuccess" />
+        <ViewDrawer @register="registerDrawerView" @success="handleSuccess" />
+    </div>
+</template>
+<script lang="ts" setup>
+    import { onBeforeMount, ref } from 'vue';
+    import { Tag } from 'ant-design-vue';
+    import { Button } from '/@/components/Button';
+
+    import { BasicTable, useTable, TableAction } from '/@/components/Table';
+
+    // import { useModal } from '/@/components/Modal';
+    import { useMessage } from '/@/hooks/web/useMessage';
+    import FormDrawer from './formDrawer.vue';
+    import ViewDrawer from './viewDrawer.vue';
+    import { columns, searchFormSchema } from './data';
+
+    import { ${frontModuleName}${busNameFirstUpper}QueryPage, ${frontModuleName}${busNameFirstUpper}Remove } from '/@/api/${frontModuleName}/${frontModuleName}${busNameFirstUpper}Api';
+    import { listDictModel } from '/@/api/common';
+    import { formatDictColor, formatDictValue, commonDict } from '/@/utils';
+    import { useDrawer } from '/@/components/Drawer';
+
+    <% for(var i = 0; i < configList.~size; i++) { %>
+    <% if(configList[i].dictTypeCode!=null) {%>
+    const  ${configList[i].fieldNameCamelCase}Options = ref();
+        <% } %>
+    <% } %>
+    onBeforeMount(async () => {
+    <% for(var i = 0; i < configList.~size; i++) { %>
+    <% if(configList[i].dictTypeCode!=null) {%>
+     ${configList[i].fieldNameCamelCase}Options.value = await listDictModel({ dictCode: '${configList[i].dictTypeCode}' });
+        <% } %>
+    <% } %>
+    });
+
+    const { createConfirm, createMessage } = useMessage();
+    // const [registerModal, { openModal }] = useModal();
+    const [registerDrawer, { openDrawer }] = useDrawer();
+    const [registerDrawerView, { openDrawer: openDrawerView }] = useDrawer();
+
+    const tableSort = ref([
+        {
+            field: 'create_time',
+            direction: 'DESC',
+        },
+    ]) as any;
+
+    const [registerTable, { reload, getSelectRowKeys }] = useTable({
+        title: '门户列表 ',
+        api: ${frontModuleName}${busNameFirstUpper}QueryPage,
+        rowKey: '${id}',
+        columns,
+        showIndexColumn: true,
+        rowSelection: { type: 'checkbox' },
+        formConfig: {
+            labelWidth: 120,
+            schemas: searchFormSchema,
+            autoSubmitOnEnter: true,
+            baseColProps: { xs: 24, sm: 12, md: 12, lg: 8 },
+            resetButtonOptions: {
+                preIcon: 'icon-delete|iconfont',
+            },
+            submitButtonOptions: {
+                preIcon: 'icon-search|iconfont',
+            },
+        },
+        useSearchForm: true,
+        bordered: true,
+        actionColumn: {
+            width: 200,
+            title: '操作',
+            dataIndex: 'action',
+        },
+        beforeFetch: handleBeforeFetch,
+        sortFn: handleSortFn,
+    });
+    // 详情按钮事件
+    function handleView(record: Recordable) {
+        console.log(record);
+        openDrawerView(true, {
+            record,
+        });
+    }
+
+    // 新增按钮事件
+    function handleCreate() {
+        openDrawer(true, {
+            isUpdate: false,
+        });
+    }
+
+    // 编辑按钮事件
+    function handleEdit(record: Recordable) {
+        openDrawer(true, {
+            record,
+            isUpdate: true,
+        });
+    }
+
+    // 删除按钮事件
+    async function handleDelete(record: Recordable) {
+        if (record) {
+            await ${frontModuleName}${busNameFirstUpper}Remove([record.${id}]);
+            createMessage.success('删除成功!');
+            await reload();
+        } else {
+            createConfirm({
+                content: '你确定要删除?',
+                iconType: 'warning',
+                onOk: async () => {
+                const keys = getSelectRowKeys();
+            await ${frontModuleName}${busNameFirstUpper}Remove(keys);
+            createMessage.success('删除成功!');
+            await reload();
+        },
+        });
+        }
+    }
+    // 表格点击字段排序
+    function handleSortFn(sortInfo) {
+        if (sortInfo?.order && sortInfo?.columnKey) {
+            // 默认单列排序
+            tableSort.value = [
+                {
+                    field: sortInfo.columnKey,
+                    direction: sortInfo.order.replace(/(\w+)(end)/g, '$1').toUpperCase(),
+                },
+            ];
+        }
+    }
+
+    // 表格请求之前,对参数进行处理, 添加默认 排序
+    function handleBeforeFetch(params) {
+        return { ...params, orders: tableSort.value };
+    }
+
+    // 弹窗回调事件
+    async function handleSuccess({ isUpdate, values }) {
+        console.log(isUpdate);
+        console.log(values);
+        await reload();
+    }
+</script>

+ 3 - 3
tr-modules/tr-module-gen/src/main/resources/frontend/viewDrawer.vue.btl

@@ -4,7 +4,7 @@
             destroyOnClose
             @register="registerDrawer"
             :title="getTitle"
-            :width="width"
+            :width="width">
         <Description @register="registerDesc" :data="descData" />
     </BasicDrawer>
 </template>
@@ -14,7 +14,7 @@
     import { Description, useDescription } from '/@/components/Description';
     import { viewSchema } from './data';
 
-    import { ${frontModuleName}${busNameFirstUpper}Detail,${frontModuleName}${busNameFirstUpper}Remove } from '/@/api/${frontModuleName}/${frontModuleName}${busNameFirstUpper}Api';
+    import { ${frontModuleName}${busNameFirstUpper}Detail } from '/@/api/${frontModuleName}/${frontModuleName}${busNameFirstUpper}Api';
     import { listDictModel } from '/@/api/common';
     import { formatDictValue } from '/@/utils';
 
@@ -36,7 +36,7 @@
     });
     const [registerDrawer] = useDrawerInner(async data => {
         console.log('::::::::::', data.record);
-        const resData = await ${backendModuleName}${busNameFirstUpper}Detail(data.record.id);
+        const resData = await ${frontModuleName}${busNameFirstUpper}Detail(data.record.id);
         descData.value = {
             ...resData,
             <% for(var i = 0; i < configList.~size; i++) { %>

+ 2 - 3
tr-modules/tr-module-system/pom.xml

@@ -64,9 +64,8 @@
         </dependency>
 
         <dependency>
-            <groupId>cn.dev33</groupId>
-            <artifactId>sa-token-oauth2</artifactId>
-            <version>1.34.0</version>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-module-system-api</artifactId>
         </dependency>
     </dependencies>
 </project>

+ 2 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/mapper/user/SysMenuMapper.java

@@ -20,6 +20,8 @@ public interface SysMenuMapper {
 
     List<SysMenuDTO> toSysMenuDTOList(List<SysMenuPO> source);
 
+    List<SysMenuPO> toSysMenuPOList(List<SysMenuDTO> source);
+
     SysMenuDTO toSysMenuDTO(SysMenuPO source);
 
     SysMenuPO toSysMenuPO(SysMenuDTO source);

+ 1 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/service/CurrentUserService.java

@@ -69,6 +69,7 @@ public class CurrentUserService {
                 .values()
                 .stream()
                 .filter(menu -> !StrUtil.equals(MenuEnum.button.name(), menu.getMenuType()))
+                .filter(menu-> Boolean.TRUE.equals(menu.getVisible()))
                 .map(menuService::convertToRoute)
                 .collect(Collectors.toList());
     }

+ 3 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/SysMenuQueryDTO.java

@@ -22,6 +22,9 @@ public class SysMenuQueryDTO implements Serializable {
     @ApiModelProperty("菜单名称")
     private String name;
 
+    @ApiModelProperty("菜单类型")
+    private String menuType;
+
     @ApiModelProperty("菜单状态")
     private Boolean disable;
 }

+ 122 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/provider/SysMenuApiProvider.java

@@ -0,0 +1,122 @@
+package cn.tr.module.sys.user.provider;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.lang.Pair;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.tr.core.exception.ServiceException;
+import cn.tr.core.exception.TRExcCode;
+import cn.tr.module.api.sys.user.SysMenuApi;
+import cn.tr.module.sys.mapper.user.SysMenuMapper;
+import cn.tr.module.sys.user.dto.SysMenuDTO;
+import cn.tr.module.sys.user.po.SysMenuPO;
+import cn.tr.module.sys.user.repository.SysMenuRepository;
+import cn.tr.module.sys.user.service.ISysMenuService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName : SysMenuApiProvider
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月14日
+ */
+@Component
+@AllArgsConstructor
+public class SysMenuApiProvider implements SysMenuApi {
+    private final SysMenuRepository menuRepository;
+    private final ISysMenuService menuService;
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Pair<String,String> addForGenMenu(String parentId, String busName, String frontModuleName,String backModuleName,String permissionPrefix, String functionName) {
+        List<String> paths = new ArrayList<>();
+        List<SysMenuDTO> treeMenu = menuService.buildTree(menuService.findAllMenu());
+        List<SysMenuDTO> flatTree = menuService.flatTree(treeMenu);
+        Map<String, SysMenuDTO> menuMapById = flatTree.stream()
+                .collect(Collectors.groupingBy(SysMenuDTO::getId, Collectors.collectingAndThen(Collectors.toList(), CollUtil::getFirst)));
+        //参数校验
+        parentId=StrUtil.isEmpty(parentId)?"0":parentId;
+        if(!StrUtil.equals(parentId,"0")) {
+            SysMenuDTO parentMenu = menuMapById.get(parentId);
+            if(ObjectUtil.isEmpty(parentMenu)) {
+                throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,String.format("上级菜单不存在,id值为:{%s}", parentId));
+            }
+        }else {
+            while (!StrUtil.equals(parentId,"0")||StrUtil.isEmpty(parentId)){
+                SysMenuDTO parentMenu = menuMapById.get(parentId);
+                if(StrUtil.isNotEmpty(parentMenu.getRoutePath())){
+                    paths.add(0,parentMenu.getRoutePath());
+                }
+                parentId=parentMenu.getParentId();
+            }
+        }
+        String routePath=frontModuleName+StrUtil.upperFirst(busName);
+        String prefix="/"+frontModuleName+"/"+CollectionUtil.join(paths,"/");
+        String componentPath=prefix+busName+"/index";
+        SysMenuPO menu = menuRepository.selectOne(new LambdaQueryWrapper<SysMenuPO>()
+                .eq(SysMenuPO::getName, functionName)
+                .eq(SysMenuPO::getMenuType, "menu"));
+        //是否是新菜单,默认否
+        boolean insert=false;
+        if(menu==null){
+            insert=true;
+            menu=new SysMenuPO();
+        }
+
+
+        // 插入新菜单
+        menu.setMenuType("menu");
+        menu.setName(functionName);
+        menu.setRoutePath(routePath);
+        menu.setComponent(componentPath);
+        menu.setParentId(parentId);
+        menu.setKeepalive(true);
+        menu.setLinkExternal(false);
+        menu.setVisible(true);
+        menu.setFrame(true);
+        menu.setDisable(false);
+        menu.setIcon("appstore-outlined");
+        menu.setSort(999);
+        SysMenuDTO menuDTO = SysMenuMapper.INSTANCE.toSysMenuDTO(menu);
+        if(insert){
+            menuService.insertSysMenu(menuDTO);
+            menu = menuRepository.selectOne(new LambdaQueryWrapper<SysMenuPO>()
+                            .eq(SysMenuPO::getName, functionName)
+                            .eq(SysMenuPO::getMenuType, "menu"));
+            addForGenButton(menu.getId(),permissionPrefix,functionName);
+
+        }else {
+            menuService.updateSysMenuById(menuDTO);
+        }
+        return Pair.of(menu.getId(), componentPath);
+    }
+
+    private void addForGenButton(String menuId, String permissionPrefix, String functionName) {
+        ArrayList<SysMenuDTO> menus = CollectionUtil.newArrayList(createButton(functionName + "查询", permissionPrefix + "query", 1, menuId),
+                createButton(functionName + "新增", permissionPrefix + "add", 2, menuId),
+                createButton(functionName + "修改", permissionPrefix + "edit", 3, menuId),
+                createButton(functionName + "删除", permissionPrefix + "remove", 4, menuId));
+        menuService.insertBatchSysMenu(menus);
+    }
+
+    private SysMenuDTO createButton(String name,String permission,int sort,String parentId){
+        SysMenuDTO menu = new SysMenuDTO();
+        menu.setSort(sort);
+        menu.setName(name);
+        menu.setPermission(permission);
+        menu.setMenuType("button");
+        menu.setParentId(parentId);
+        menu.setKeepalive(false);
+        menu.setLinkExternal(false);
+        menu.setVisible(true);
+        menu.setFrame(false);
+        menu.setDisable(false);
+        return menu;
+    }
+}

+ 18 - 12
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/ISysMenuService.java

@@ -54,29 +54,35 @@ public interface ISysMenuService extends ITreeService {
 
 
     /**
-     * 根据id查询操作用户
-     * @param id 用户id
-     * @return 用户
+     * 根据id查询操作菜单
+     * @param id 菜单id
+     * @return 菜单
      */
     SysMenuDTO selectSysMenuById(String id);
 
     /**
-     * 更新用户
-     * @param source 更新用户
+     * 更新菜单
+     * @param source 更新菜单
      * @return true:更新成功
      */
     boolean updateSysMenuById(SysMenuDTO source);
 
     /**
-     * 新增用户
-     * @param source 新增用户
+     * 新增菜单
+     * @param source 新增菜单
      * @return true:新增成功
      */
     boolean insertSysMenu(SysMenuDTO source);
 
     /**
-     * 根据id删除用户
-     * @param ids 用户id
+     * 批量新增菜单
+     * @param source 新增菜单
+     * @return true:新增成功
+     */
+    boolean insertBatchSysMenu(List<SysMenuDTO> source);
+    /**
+     * 根据id删除菜单
+     * @param ids 菜单id
      * @return 删除数量
      */
     int deleteSysMenuByIds(Collection<String> ids);
@@ -89,14 +95,14 @@ public interface ISysMenuService extends ITreeService {
 
     /**
      * 查询用户的菜单
-     * @param userId 用户id
+     * @param userId 菜单id
      * @return
      */
     Set<SysMenuDTO>  findUserMenus(String userId);
 
     /**
-     * 查询用户的权限
-     * @param userId 用户id
+     * 查询菜单的权限
+     * @param userId 菜单id
      * @return
      */
     default Set<String>  findUserPermission(String userId){

+ 46 - 16
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/impl/SysMenuServiceImpl.java

@@ -1,10 +1,12 @@
 package cn.tr.module.sys.user.service.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.tr.core.exception.ServiceException;
 import cn.tr.core.exception.TRExcCode;
+import cn.tr.core.tree.TreeNode;
 import cn.tr.module.sys.mapper.user.SysMenuMapper;
 import cn.tr.module.sys.tenant.service.ISysTenantPackageMenuService;
 import cn.tr.module.sys.tenant.service.ISysTenantService;
@@ -16,6 +18,7 @@ import cn.tr.module.sys.user.po.SysMenuPO;
 import cn.tr.module.sys.user.repository.SysMenuRepository;
 import cn.tr.module.sys.user.service.*;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
@@ -23,6 +26,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.util.*;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * @ClassName : SysMenuServiceImpl
@@ -31,7 +35,7 @@ import java.util.stream.Collectors;
  * @Date: 2023年03月31日
  */
 @Service
-public class SysMenuServiceImpl implements ISysMenuService {
+public class SysMenuServiceImpl extends ServiceImpl<SysMenuRepository,SysMenuPO> implements ISysMenuService {
     @Autowired
     private SysMenuRepository menuRepository;
     @Autowired
@@ -54,10 +58,6 @@ public class SysMenuServiceImpl implements ISysMenuService {
     @Lazy
     private ISysTenantService tenantService;
 
-    @Autowired
-    @Lazy
-    private ISysRoleService roleService;
-
     @Autowired
     @Lazy
     private ISysMenuService self;
@@ -67,12 +67,8 @@ public class SysMenuServiceImpl implements ISysMenuService {
         SysMenuMapper.INSTANCE.toSysMenuDTOList( menuRepository.selectList(new LambdaQueryWrapper<SysMenuPO>()
                 .like(StrUtil.isNotEmpty(query.getName()), SysMenuPO::getName, query.getName())
                 .eq(ObjectUtil.isNotNull(query.getDisable()), SysMenuPO::getDisable, query.getDisable())
+                .eq(ObjectUtil.isNotNull(query.getMenuType()), SysMenuPO::getMenuType, query.getMenuType())
                 .eq(ObjectUtil.isNotNull(query.getParentId()), SysMenuPO::getParentId, query.getParentId())));
-//        if(CollectionUtil.isNotEmpty(selectResult)){
-//            //取与当前租户菜单交集
-//            Set<SysMenuDTO> tenantMenus = tenantService.currentTenantMenus();
-//            return new ArrayList<>(CollectionUtil.intersection(tenantMenus,selectResult));
-//        }
         return selectResult;
     }
 
@@ -101,24 +97,45 @@ public class SysMenuServiceImpl implements ISysMenuService {
         return result;
     }
 
+    @Override
+    public boolean insertBatchSysMenu(List<SysMenuDTO> source) {
+        boolean result = this.saveBatch(SysMenuMapper.INSTANCE.toSysMenuPOList(source));
+        if (result) {
+            refreshCache(source.stream().map(SysMenuDTO::getId).collect(Collectors.toSet()));
+        }
+        return result;
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public int deleteSysMenuByIds(Collection<String> ids) {
-        List<SysMenuPO> menus = menuRepository.selectBatchIds(ids);
+        List<SysMenuDTO> treeMenu = buildTree(self.findAllMenu());
+        List<SysMenuDTO> flatTree = flatTree(treeMenu);
+        Map<String, SysMenuDTO> menuMapById = flatTree.stream()
+                .collect(Collectors.groupingBy(SysMenuDTO::getId, Collectors.collectingAndThen(Collectors.toList(), CollUtil::getFirst)));
+        Set<String> allDelMenuIds = new HashSet<>();
+        for (String id : ids) {
+            SysMenuDTO existMenu = menuMapById.get(id);
+            findAllChildrenId(existMenu,allDelMenuIds);
+        }
+        if (CollectionUtil.isEmpty(allDelMenuIds)) {
+            return CollectionUtil.size(allDelMenuIds);
+        }
+        List<SysMenuPO> menus = menuRepository.selectBatchIds(allDelMenuIds);
         if(CollectionUtil.isEmpty(menus)){
-            return CollectionUtil.size(ids);
+            return CollectionUtil.size(allDelMenuIds);
         }
         //删除相应缓存
-        Set<String> roleIds = roleMenuService.findRoleByMenuId(ids,false);
+        Set<String> roleIds = roleMenuService.findRoleByMenuId(allDelMenuIds,false);
         if(CollectionUtil.isNotEmpty(roleIds)){
             throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,"所选菜单已与角色相关联,请取消关联后再删除菜单");
         }
-        Set<String> packageIds = tenantPackageMenuService.findPackageIdByMenuId(ids,false);
+        Set<String> packageIds = tenantPackageMenuService.findPackageIdByMenuId(allDelMenuIds,false);
         if(CollectionUtil.isNotEmpty(packageIds)){
             throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,"所选菜单已与租户套餐相关联,请取消关联后再删除菜单");
         }
-        refreshCache(ids);
-        return menuRepository.deleteBatchIds(ids);
+        refreshCache(allDelMenuIds);
+        return menuRepository.deleteBatchIds(allDelMenuIds);
     }
 
     @Override
@@ -161,4 +178,17 @@ public class SysMenuServiceImpl implements ISysMenuService {
         Set<String> portalIds=portalMenuService.findPortalIdByMenuId(source,true);
         portalIds.parallelStream().forEach(portalMenuService::delCacheMenusByPortalId);
     }
+
+    private void findAllChildrenId(TreeNode<String> children, Set<String> result){
+        if(children==null){
+            return;
+        }
+        result.add(children.getId());
+        List<TreeNode<String>> childrenNode = children.getChildren();
+        if(CollectionUtil.isNotEmpty(childrenNode)){
+            for (TreeNode<String> treeNode : childrenNode) {
+                findAllChildrenId(treeNode,result);
+            }
+        }
+    }
 }

+ 10 - 0
tr-plugins/tr-spring-boot-starter-plugin-mybatis/src/main/java/cn/tr/plugin/mybatis/service/ITreeService.java

@@ -23,4 +23,14 @@ public interface ITreeService {
         return buildTree(sources,null);
     }
 
+    /**
+     * 将树结构展开
+     * @param sources
+     * @param <T>
+     * @return
+     */
+    default  <T extends TreeNode> List<T> flatTree(Collection<T> sources){
+        return TreeUtil.flatTree(sources);
+    }
+
 }

+ 1 - 0
tr-test/src/main/resources/application-doc.yml

@@ -13,6 +13,7 @@ knife4j:
         api-rule: package
         api-rule-resources:
           - cn.tr.module.sys
+          - cn.tr.module.file
       gen:
         group-name: 代码生成器
         api-rule: package