From f172154800170e897b1c59c46004e941c90eca30 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Thu, 28 May 2026 14:38:59 +0800 Subject: [PATCH] =?UTF-8?q?feat(pigx-codegen):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=AE=9E=E4=BD=93=E7=94=9F=E6=88=90=E5=88=B0=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=20api=20=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 DataModelConstants 定义 API_PATH、ENTITY_PATH 等实体路径常量 - 实现 setEntityModel 方法,自动识别 `-biz` 模块同级 `-api` 模块,将实体生成路径和包名切换到 api 模块 - 更新 gen_template.sql 代码生成模板,使用 entityClassName、entityPath 等动态变量替代硬编码包路径 - 扩展 isSpringBoot3 判断逻辑,兼容 SpringBoot 4.x 主版本 --- db/0pigxx_boot.sql | 56 ++++----- db/8pigxx_codegen.sql | 56 ++++----- .../service/impl/GeneratorServiceImpl.java | 108 +++++++++++++++++- .../pigx/codegen/util/DataModelConstants.java | 12 ++ 4 files changed, 173 insertions(+), 59 deletions(-) diff --git a/db/0pigxx_boot.sql b/db/0pigxx_boot.sql index e558b579f..382c9be31 100644 --- a/db/0pigxx_boot.sql +++ b/db/0pigxx_boot.sql @@ -2732,46 +2732,46 @@ CREATE TABLE `gen_template` ( -- Records of gen_template -- ---------------------------- BEGIN; -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (1,'ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.list(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.save(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateById(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeBatchByIds(CollUtil.toList(ids)));\n }\n\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (2,'ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\n\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (3,'ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (4,'实体V2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/entity/${ClassName}Entity.java','实体V2026001','package ${package}.${moduleName}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (5,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (6,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (1,'ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.list(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.save(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateById(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeBatchByIds(CollUtil.toList(ids)));\n }\n\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (2,'ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${entityClassName};\n\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (3,'ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (4,'实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (5,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (6,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (7,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (8,'api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n\n#if($ChildClassName)\n// ========== 子表相关接口 ==========\n\n/**\n * 删除子表数据\n * @param ids - 要删除的子表数据ID数组\n * @returns Promise<操作结果>\n */\nexport function delChildObj(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/child\',\n method: \'delete\',\n data: ids\n })\n}\n#end\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (9,'表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','表格V2026001','\n\n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (10,'表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','表单V2026001','\n\n ','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (11,'主子ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','主子ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport com.github.yulichang.extension.mapping.base.MPJDeepService;\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\n\npublic interface ${ClassName}Service extends MPJDeepService<${ClassName}Entity> {\n Boolean saveDeep(${ClassName}Entity ${className});\n\n Boolean updateDeep(${ClassName}Entity ${className});\n\n Boolean removeDeep(Long[] ids);\n\n Boolean removeChild(Long[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (12,'主子ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','主子ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport org.springframework.transaction.annotation.Transactional;\nimport lombok.RequiredArgsConstructor;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveDeep(${ClassName}Entity ${className}) {\n baseMapper.insert(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert( ${childClassName});\n }\n\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateDeep(${ClassName}Entity ${className}) {\n baseMapper.updateById(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n#set($getChildPkName=$str.getProperty(${pk.attrName}))\n if (Objects.isNull(${childClassName}.$getChildPkName())) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert(${childClassName});\n } else {\n ${childClassName}Mapper.updateById(${childClassName});\n }\n }\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeDeep(Long[] ids) {\n baseMapper.deleteBatchIds(CollUtil.toList(ids));\n ${childClassName}Mapper.delete(Wrappers.<${ChildClassName}Entity>lambdaQuery().in(${ChildClassName}Entity::$str.getProperty($childField), ids));\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeChild(Long[] ids) {\n ${childClassName}Mapper.deleteBatchIds(CollUtil.toList(ids));\n return Boolean.TRUE;\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (13,'主实体V2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/entity/${ClassName}Entity.java','主实体V2026001','package ${package}.${moduleName}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\nimport org.apache.fesod.sheet.annotation.ExcelIgnore;\nimport com.github.yulichang.annotation.EntityMapping;\nimport java.util.List;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n @TableField(typeHandler = JacksonTypeHandler.class)\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n @ExcelIgnore\n @TableField(exist = false)\n @EntityMapping(thisField = \"$mainField\", joinField = \"$childField\")\n private List<${ChildClassName}Entity> ${childClassName}List;\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (14,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (15,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (11,'主子ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','主子ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport com.github.yulichang.extension.mapping.base.MPJDeepService;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\n\npublic interface ${ClassName}Service extends MPJDeepService<${ClassName}Entity> {\n Boolean saveDeep(${ClassName}Entity ${className});\n\n Boolean updateDeep(${ClassName}Entity ${className});\n\n Boolean removeDeep(Long[] ids);\n\n Boolean removeChild(Long[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (12,'主子ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','主子ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport org.springframework.transaction.annotation.Transactional;\nimport lombok.RequiredArgsConstructor;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveDeep(${ClassName}Entity ${className}) {\n baseMapper.insert(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert( ${childClassName});\n }\n\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateDeep(${ClassName}Entity ${className}) {\n baseMapper.updateById(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n#set($getChildPkName=$str.getProperty(${pk.attrName}))\n if (Objects.isNull(${childClassName}.$getChildPkName())) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert(${childClassName});\n } else {\n ${childClassName}Mapper.updateById(${childClassName});\n }\n }\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeDeep(Long[] ids) {\n baseMapper.deleteBatchIds(CollUtil.toList(ids));\n ${childClassName}Mapper.delete(Wrappers.<${ChildClassName}Entity>lambdaQuery().in(${ChildClassName}Entity::$str.getProperty($childField), ids));\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeChild(Long[] ids) {\n ${childClassName}Mapper.deleteBatchIds(CollUtil.toList(ids));\n return Boolean.TRUE;\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (13,'主实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','主实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\nimport org.apache.fesod.sheet.annotation.ExcelIgnore;\nimport com.github.yulichang.annotation.EntityMapping;\nimport java.util.List;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n @TableField(typeHandler = JacksonTypeHandler.class)\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n @ExcelIgnore\n @TableField(exist = false)\n @EntityMapping(thisField = \"$mainField\", joinField = \"$childField\")\n private List<${ChildClassName}Entity> ${childClassName}List;\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (14,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (15,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (16,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (17,'api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n\n#if($ChildClassName)\n// ========== 子表相关接口 ==========\n\n/**\n * 删除子表数据\n * @param ids - 要删除的子表数据ID数组\n * @returns Promise<操作结果>\n */\nexport function delChildObj(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/child\',\n method: \'delete\',\n data: ids\n })\n}\n#end\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (18,'子实体V2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/entity/${ChildClassName}Entity.java','子实体V2026001','package ${package}.${moduleName}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(\"${childTableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (19,'主子ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','主子ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.listDeep(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.saveDeep(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateDeep(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeDeep(ids));\n }\n\n /**\n * 通过id删除${tableComment}子表数据\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}子表数据\" , description = \"通过id删除${tableComment}子表数据\" )\n @SysLog(\"通过id删除${tableComment}子表数据\" )\n @DeleteMapping(\"/child\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeChild(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeChild(ids));\n }\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (18,'子实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ChildClassName}Entity.java','子实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(\"${childTableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (19,'主子ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','主子ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.listDeep(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.saveDeep(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateDeep(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeDeep(ids));\n }\n\n /**\n * 通过id删除${tableComment}子表数据\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}子表数据\" , description = \"通过id删除${tableComment}子表数据\" )\n @SysLog(\"通过id删除${tableComment}子表数据\" )\n @DeleteMapping(\"/child\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeChild(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeChild(ids));\n }\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (20,'主子表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','主子表单V2026001','\n\n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (21,'主子表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','主子表格V2026001','\n\n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (22,'子MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV2026001','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (23,'树形ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','树形ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 获取树形列表\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"获取树形列表\" , description = \"获取树形列表\" )\n @GetMapping(\"/tree\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Tree(@ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.buildTree(wrapper));\n }\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.list(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.save(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateById(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeBatchByIds(CollUtil.toList(ids)));\n }\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n} \n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (24,'树形ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','树形ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper);\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n boolean removeBatchByIds(List<${pk.attrType}> ids);\n} ','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (25,'树形ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','树形ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper) {\n // 查询所有数据\n List<${ClassName}Entity> allList = list(wrapper);\n \n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n // 转换为TreeNode\n List> collect = allList.stream().map(getNodeFunction()).toList();\n\n // 使用TreeUtil构建树形结构,根节点ID为0\n#if($pk.attrType == \'Long\')\n return TreeUtil.build(collect, 0L);\n#else\n return TreeUtil.build(collect, 0);\n#end\n }\n\n /**\n * 获取TreeNode转换函数\n * @return TreeNode转换函数\n */\n @NotNull\n private Function<${ClassName}Entity, TreeNode<${pk.attrType}>> getNodeFunction() {\n return entity -> {\n TreeNode<${pk.attrType}> node = new TreeNode<>();\n node.setId(entity.$str.getProperty($pk.attrName)());\n node.setName(entity.$str.getProperty($nameField)());\n node.setParentId(entity.$str.getProperty($parentField)() != null ? entity.$str.getProperty($parentField)() : 0L);\n\n // 扩展属性\n Map extra = new HashMap<>();\n#foreach($field in $fieldList)\n extra.put(${ClassName}Entity.Fields.${field.attrName}, entity.$str.getProperty($field.attrName)());\n#end\n node.setExtra(extra);\n return node;\n };\n }\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public boolean removeBatchByIds(List<${pk.attrType}> ids) {\n if (CollUtil.isEmpty(ids)) {\n return true;\n }\n\n // 获取所有要删除的ID(包括子节点)\n List<${pk.attrType}> allDeleteIds = new ArrayList<>(ids);\n \n for (${pk.attrType} id : ids) {\n List<${pk.attrType}> childIds = getChildIdsRecursive(id);\n allDeleteIds.addAll(childIds);\n }\n\n // 去重\n allDeleteIds = allDeleteIds.stream().distinct().collect(Collectors.toList());\n\n // 批量删除\n return super.removeBatchByIds(allDeleteIds);\n }\n\n /**\n * 递归获取所有子节点ID\n * @param parentId 父节点ID\n * @return 子节点ID列表\n */\n private List<${pk.attrType}> getChildIdsRecursive(${pk.attrType} parentId) {\n List<${pk.attrType}> childIds = new ArrayList<>();\n \n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n wrapper.eq(${ClassName}Entity::$str.getProperty($parentField), parentId);\n wrapper.select(${ClassName}Entity::$str.getProperty($pk.attrName));\n \n List<${ClassName}Entity> children = list(wrapper);\n \n for (${ClassName}Entity child : children) {\n ${pk.attrType} childId = child.$str.getProperty($pk.attrName)();\n childIds.add(childId);\n // 递归获取子节点的子节点\n childIds.addAll(getChildIdsRecursive(childId));\n }\n \n return childIds;\n }\n} ','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (26,'树形实体V2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/entity/${ClassName}Entity.java','树形实体V2026001','package ${package}.${moduleName}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.FieldNameConstants;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@FieldNameConstants\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (27,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (28,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (22,'子MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV2026001','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (23,'树形ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','树形ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 获取树形列表\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"获取树形列表\" , description = \"获取树形列表\" )\n @GetMapping(\"/tree\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Tree(@ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.buildTree(wrapper));\n }\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.list(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.save(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateById(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeBatchByIds(CollUtil.toList(ids)));\n }\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n} \n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (24,'树形ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','树形ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${entityClassName};\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper);\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n boolean removeBatchByIds(List<${pk.attrType}> ids);\n} ','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (25,'树形ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','树形ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper) {\n // 查询所有数据\n List<${ClassName}Entity> allList = list(wrapper);\n \n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n // 转换为TreeNode\n List> collect = allList.stream().map(getNodeFunction()).toList();\n\n // 使用TreeUtil构建树形结构,根节点ID为0\n#if($pk.attrType == \'Long\')\n return TreeUtil.build(collect, 0L);\n#else\n return TreeUtil.build(collect, 0);\n#end\n }\n\n /**\n * 获取TreeNode转换函数\n * @return TreeNode转换函数\n */\n @NotNull\n private Function<${ClassName}Entity, TreeNode<${pk.attrType}>> getNodeFunction() {\n return entity -> {\n TreeNode<${pk.attrType}> node = new TreeNode<>();\n node.setId(entity.$str.getProperty($pk.attrName)());\n node.setName(entity.$str.getProperty($nameField)());\n node.setParentId(entity.$str.getProperty($parentField)() != null ? entity.$str.getProperty($parentField)() : 0L);\n\n // 扩展属性\n Map extra = new HashMap<>();\n#foreach($field in $fieldList)\n extra.put(${ClassName}Entity.Fields.${field.attrName}, entity.$str.getProperty($field.attrName)());\n#end\n node.setExtra(extra);\n return node;\n };\n }\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public boolean removeBatchByIds(List<${pk.attrType}> ids) {\n if (CollUtil.isEmpty(ids)) {\n return true;\n }\n\n // 获取所有要删除的ID(包括子节点)\n List<${pk.attrType}> allDeleteIds = new ArrayList<>(ids);\n \n for (${pk.attrType} id : ids) {\n List<${pk.attrType}> childIds = getChildIdsRecursive(id);\n allDeleteIds.addAll(childIds);\n }\n\n // 去重\n allDeleteIds = allDeleteIds.stream().distinct().collect(Collectors.toList());\n\n // 批量删除\n return super.removeBatchByIds(allDeleteIds);\n }\n\n /**\n * 递归获取所有子节点ID\n * @param parentId 父节点ID\n * @return 子节点ID列表\n */\n private List<${pk.attrType}> getChildIdsRecursive(${pk.attrType} parentId) {\n List<${pk.attrType}> childIds = new ArrayList<>();\n \n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n wrapper.eq(${ClassName}Entity::$str.getProperty($parentField), parentId);\n wrapper.select(${ClassName}Entity::$str.getProperty($pk.attrName));\n \n List<${ClassName}Entity> children = list(wrapper);\n \n for (${ClassName}Entity child : children) {\n ${pk.attrType} childId = child.$str.getProperty($pk.attrName)();\n childIds.add(childId);\n // 递归获取子节点的子节点\n childIds.addAll(getChildIdsRecursive(childId));\n }\n \n return childIds;\n }\n} ','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (26,'树形实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','树形实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.FieldNameConstants;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@FieldNameConstants\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (27,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (28,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (29,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (30,'树形api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','树形api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 树形表格CRUD接口 ==========\n\n/**\n * 获取树形列表数据\n * @param query - 查询参数对象\n * @returns Promise<树形数据>\n */\nexport function fetchTreeList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据(递归删除子节点)\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (31,'树形表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','树形表格V2026001','\n\n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (32,'树形表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','树形表单V2026001','\n\n \n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (33,'ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','ControllerV2026001','package ${package}.${moduleName}.controller;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($childFieldGetter = $str.getProperty($childField))\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n#if($childField)\n if (Objects.nonNull(${className}.$childFieldGetter())) {\n wrapper.eq(${ClassName}Entity::$str.getProperty($childField), ${className}.$childFieldGetter());\n }\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.list(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.save(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateById(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeBatchByIds(CollUtil.toList(ids)));\n }\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n\n /**\n * 获取树形列表\n * @param ${childClassName} 树节点查询条件\n * @return 树形数据\n */\n @Operation(summary = \"获取树形列表\" , description = \"获取树形列表\" )\n @GetMapping(\"/tree\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getTree(@ParameterObject ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.buildTree(Wrappers.lambdaQuery(${childClassName})));\n }\n\n /**\n * 通过条件查询树节点\n * @param ${childClassName} 树节点查询条件\n * @return 树节点列表\n */\n @Operation(summary = \"通过条件查询树节点\" , description = \"通过条件查询树节点\" )\n @GetMapping(\"/tree/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getTreeDetails(@ParameterObject ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.getTreeDetails(${childClassName}));\n }\n\n /**\n * 新增树节点\n * @param ${childClassName} 树节点\n * @return R\n */\n @Operation(summary = \"新增树节点\" , description = \"新增树节点\" )\n @SysLog(\"新增树节点\" )\n @PostMapping(\"/tree\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R saveTree(@RequestBody ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.saveTree(${childClassName}));\n }\n\n /**\n * 修改树节点\n * @param ${childClassName} 树节点\n * @return R\n */\n @Operation(summary = \"修改树节点\" , description = \"修改树节点\" )\n @SysLog(\"修改树节点\" )\n @PutMapping(\"/tree\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateTree(@RequestBody ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.updateTree(${childClassName}));\n }\n\n /**\n * 通过id删除树节点\n * @param ids 树节点ID列表\n * @return R\n */\n @Operation(summary = \"通过id删除树节点\" , description = \"通过id删除树节点\" )\n @SysLog(\"通过id删除树节点\" )\n @DeleteMapping(\"/tree\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeTree(@RequestBody ${childPkField.attrType}[] ids) {\n return R.ok(${className}Service.removeTreeByIds(ids));\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (34,'ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper);\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName});\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n Boolean saveTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n Boolean updateTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n Boolean removeTreeByIds(${childPkField.attrType}[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (35,'ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#set($childPkGetter = $str.getProperty($childPkField.attrName))\n#break\n#end\n#end\n#set($parentGetter = $str.getProperty($parentField))\n#set($childFieldGetter = $str.getProperty($childField))\n#if($childPkField.attrType == \'Long\')\n#set($treeRootId = \'0L\')\n#elseif($childPkField.attrType == \'String\')\n#set($treeRootId = \'\"0\"\')\n#else\n#set($treeRootId = \'0\')\n#end\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper) {\n List<${ChildClassName}Entity> allList = ${childClassName}Mapper.selectList(wrapper);\n\n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n List> collect = allList.stream().map(getNodeFunction()).toList();\n return TreeUtil.build(collect, ${treeRootId});\n }\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n @Override\n public List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName}) {\n return ${childClassName}Mapper.selectList(Wrappers.query(${childClassName}));\n }\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(null, ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.insert(${childClassName}) > 0;\n }\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(${childClassName}.$str.getProperty($childPkField.attrName)(), ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.updateById(${childClassName}) > 0;\n }\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeTreeByIds(${childPkField.attrType}[] ids) {\n if (ArrayUtil.isEmpty(ids)) {\n return Boolean.TRUE;\n }\n\n for (${childPkField.attrType} id : ids) {\n if (hasChildNodes(id)) {\n throw new RuntimeException(\"当前节点存在子节点,不能删除\");\n }\n if (hasMainRecords(id)) {\n throw new RuntimeException(\"当前节点已关联主表数据,不能删除\");\n }\n }\n\n return ${childClassName}Mapper.deleteBatchIds(CollUtil.toList(ids)) > 0;\n }\n\n /**\n * 获取TreeNode转换函数\n * @return TreeNode转换函数\n */\n private Function<${ChildClassName}Entity, TreeNode<${childPkField.attrType}>> getNodeFunction() {\n return entity -> {\n TreeNode<${childPkField.attrType}> node = new TreeNode<>();\n node.setId(entity.$childPkGetter());\n node.setName(entity.$str.getProperty($nameField)());\n node.setParentId(normalizeParentId(entity.$parentGetter()));\n\n Map extra = new HashMap<>();\n#foreach($field in $childFieldList)\n extra.put(\"${field.attrName}\", entity.$str.getProperty($field.attrName)());\n#end\n node.setExtra(extra);\n return node;\n };\n }\n\n /**\n * 规范化父节点ID,并校验父子关系\n * @param currentId 当前节点ID\n * @param parentId 父节点ID\n * @return 规范化后的父节点ID\n */\n private ${childPkField.attrType} validateParentRelation(${childPkField.attrType} currentId, ${childPkField.attrType} parentId) {\n ${childPkField.attrType} normalizedParentId = normalizeParentId(parentId);\n\n if (Objects.nonNull(currentId) && Objects.equals(currentId, normalizedParentId)) {\n throw new RuntimeException(\"不能选择当前节点作为父节点\");\n }\n\n if (Objects.nonNull(currentId) && isDescendant(currentId, normalizedParentId)) {\n throw new RuntimeException(\"不能选择当前节点的后代节点作为父节点\");\n }\n\n return normalizedParentId;\n }\n\n /**\n * 判断候选父节点是否是当前节点的后代节点\n * @param currentId 当前节点ID\n * @param candidateParentId 候选父节点ID\n * @return 是否为后代节点\n */\n private boolean isDescendant(${childPkField.attrType} currentId, ${childPkField.attrType} candidateParentId) {\n if (Objects.isNull(currentId) || Objects.isNull(candidateParentId) || Objects.equals(candidateParentId, ${treeRootId})) {\n return false;\n }\n\n Set<${childPkField.attrType}> visited = new HashSet<>();\n ${ChildClassName}Entity parent = ${childClassName}Mapper.selectById(candidateParentId);\n\n while (Objects.nonNull(parent) && visited.add(parent.$childPkGetter())) {\n if (Objects.equals(currentId, parent.$childPkGetter())) {\n return true;\n }\n\n ${childPkField.attrType} nextParentId = parent.$parentGetter();\n if (Objects.isNull(nextParentId) || Objects.equals(nextParentId, ${treeRootId})) {\n return false;\n }\n parent = ${childClassName}Mapper.selectById(nextParentId);\n }\n\n return false;\n }\n\n /**\n * 判断是否存在子节点\n * @param parentId 父节点ID\n * @return 是否存在子节点\n */\n private boolean hasChildNodes(${childPkField.attrType} parentId) {\n LambdaQueryWrapper<${ChildClassName}Entity> wrapper = Wrappers.lambdaQuery();\n wrapper.eq(${ChildClassName}Entity::$parentGetter, parentId);\n return ${childClassName}Mapper.selectCount(wrapper) > 0;\n }\n\n /**\n * 判断是否存在关联主表数据\n * @param treeId 树节点ID\n * @return 是否存在主表数据\n */\n private boolean hasMainRecords(${childPkField.attrType} treeId) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n wrapper.eq(${ClassName}Entity::$childFieldGetter, treeId);\n return baseMapper.selectCount(wrapper) > 0;\n }\n\n /**\n * 规范化父节点ID\n * @param parentId 父节点ID\n * @return 规范化后的父节点ID\n */\n private ${childPkField.attrType} normalizeParentId(${childPkField.attrType} parentId) {\n return Objects.nonNull(parentId) ? parentId : ${treeRootId};\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (36,'实体V2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/entity/${ClassName}Entity.java','实体V2026001','package ${package}.${moduleName}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (37,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (38,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (39,'子实体V2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/entity/${ChildClassName}Entity.java','子实体V2026001','package ${package}.${moduleName}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(\"${childTableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (40,'子MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV2026001','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (33,'ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','ControllerV2026001','package ${package}.${moduleName}.controller;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($childFieldGetter = $str.getProperty($childField))\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n#if($childField)\n if (Objects.nonNull(${className}.$childFieldGetter())) {\n wrapper.eq(${ClassName}Entity::$str.getProperty($childField), ${className}.$childFieldGetter());\n }\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.list(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.save(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateById(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeBatchByIds(CollUtil.toList(ids)));\n }\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n\n /**\n * 获取树形列表\n * @param ${childClassName} 树节点查询条件\n * @return 树形数据\n */\n @Operation(summary = \"获取树形列表\" , description = \"获取树形列表\" )\n @GetMapping(\"/tree\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getTree(@ParameterObject ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.buildTree(Wrappers.lambdaQuery(${childClassName})));\n }\n\n /**\n * 通过条件查询树节点\n * @param ${childClassName} 树节点查询条件\n * @return 树节点列表\n */\n @Operation(summary = \"通过条件查询树节点\" , description = \"通过条件查询树节点\" )\n @GetMapping(\"/tree/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getTreeDetails(@ParameterObject ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.getTreeDetails(${childClassName}));\n }\n\n /**\n * 新增树节点\n * @param ${childClassName} 树节点\n * @return R\n */\n @Operation(summary = \"新增树节点\" , description = \"新增树节点\" )\n @SysLog(\"新增树节点\" )\n @PostMapping(\"/tree\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R saveTree(@RequestBody ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.saveTree(${childClassName}));\n }\n\n /**\n * 修改树节点\n * @param ${childClassName} 树节点\n * @return R\n */\n @Operation(summary = \"修改树节点\" , description = \"修改树节点\" )\n @SysLog(\"修改树节点\" )\n @PutMapping(\"/tree\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateTree(@RequestBody ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.updateTree(${childClassName}));\n }\n\n /**\n * 通过id删除树节点\n * @param ids 树节点ID列表\n * @return R\n */\n @Operation(summary = \"通过id删除树节点\" , description = \"通过id删除树节点\" )\n @SysLog(\"通过id删除树节点\" )\n @DeleteMapping(\"/tree\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeTree(@RequestBody ${childPkField.attrType}[] ids) {\n return R.ok(${className}Service.removeTreeByIds(ids));\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (34,'ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper);\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName});\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n Boolean saveTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n Boolean updateTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n Boolean removeTreeByIds(${childPkField.attrType}[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (35,'ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#set($childPkGetter = $str.getProperty($childPkField.attrName))\n#break\n#end\n#end\n#set($parentGetter = $str.getProperty($parentField))\n#set($childFieldGetter = $str.getProperty($childField))\n#if($childPkField.attrType == \'Long\')\n#set($treeRootId = \'0L\')\n#elseif($childPkField.attrType == \'String\')\n#set($treeRootId = \'\"0\"\')\n#else\n#set($treeRootId = \'0\')\n#end\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper) {\n List<${ChildClassName}Entity> allList = ${childClassName}Mapper.selectList(wrapper);\n\n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n List> collect = allList.stream().map(getNodeFunction()).toList();\n return TreeUtil.build(collect, ${treeRootId});\n }\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n @Override\n public List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName}) {\n return ${childClassName}Mapper.selectList(Wrappers.query(${childClassName}));\n }\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(null, ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.insert(${childClassName}) > 0;\n }\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(${childClassName}.$str.getProperty($childPkField.attrName)(), ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.updateById(${childClassName}) > 0;\n }\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeTreeByIds(${childPkField.attrType}[] ids) {\n if (ArrayUtil.isEmpty(ids)) {\n return Boolean.TRUE;\n }\n\n for (${childPkField.attrType} id : ids) {\n if (hasChildNodes(id)) {\n throw new RuntimeException(\"当前节点存在子节点,不能删除\");\n }\n if (hasMainRecords(id)) {\n throw new RuntimeException(\"当前节点已关联主表数据,不能删除\");\n }\n }\n\n return ${childClassName}Mapper.deleteBatchIds(CollUtil.toList(ids)) > 0;\n }\n\n /**\n * 获取TreeNode转换函数\n * @return TreeNode转换函数\n */\n private Function<${ChildClassName}Entity, TreeNode<${childPkField.attrType}>> getNodeFunction() {\n return entity -> {\n TreeNode<${childPkField.attrType}> node = new TreeNode<>();\n node.setId(entity.$childPkGetter());\n node.setName(entity.$str.getProperty($nameField)());\n node.setParentId(normalizeParentId(entity.$parentGetter()));\n\n Map extra = new HashMap<>();\n#foreach($field in $childFieldList)\n extra.put(\"${field.attrName}\", entity.$str.getProperty($field.attrName)());\n#end\n node.setExtra(extra);\n return node;\n };\n }\n\n /**\n * 规范化父节点ID,并校验父子关系\n * @param currentId 当前节点ID\n * @param parentId 父节点ID\n * @return 规范化后的父节点ID\n */\n private ${childPkField.attrType} validateParentRelation(${childPkField.attrType} currentId, ${childPkField.attrType} parentId) {\n ${childPkField.attrType} normalizedParentId = normalizeParentId(parentId);\n\n if (Objects.nonNull(currentId) && Objects.equals(currentId, normalizedParentId)) {\n throw new RuntimeException(\"不能选择当前节点作为父节点\");\n }\n\n if (Objects.nonNull(currentId) && isDescendant(currentId, normalizedParentId)) {\n throw new RuntimeException(\"不能选择当前节点的后代节点作为父节点\");\n }\n\n return normalizedParentId;\n }\n\n /**\n * 判断候选父节点是否是当前节点的后代节点\n * @param currentId 当前节点ID\n * @param candidateParentId 候选父节点ID\n * @return 是否为后代节点\n */\n private boolean isDescendant(${childPkField.attrType} currentId, ${childPkField.attrType} candidateParentId) {\n if (Objects.isNull(currentId) || Objects.isNull(candidateParentId) || Objects.equals(candidateParentId, ${treeRootId})) {\n return false;\n }\n\n Set<${childPkField.attrType}> visited = new HashSet<>();\n ${ChildClassName}Entity parent = ${childClassName}Mapper.selectById(candidateParentId);\n\n while (Objects.nonNull(parent) && visited.add(parent.$childPkGetter())) {\n if (Objects.equals(currentId, parent.$childPkGetter())) {\n return true;\n }\n\n ${childPkField.attrType} nextParentId = parent.$parentGetter();\n if (Objects.isNull(nextParentId) || Objects.equals(nextParentId, ${treeRootId})) {\n return false;\n }\n parent = ${childClassName}Mapper.selectById(nextParentId);\n }\n\n return false;\n }\n\n /**\n * 判断是否存在子节点\n * @param parentId 父节点ID\n * @return 是否存在子节点\n */\n private boolean hasChildNodes(${childPkField.attrType} parentId) {\n LambdaQueryWrapper<${ChildClassName}Entity> wrapper = Wrappers.lambdaQuery();\n wrapper.eq(${ChildClassName}Entity::$parentGetter, parentId);\n return ${childClassName}Mapper.selectCount(wrapper) > 0;\n }\n\n /**\n * 判断是否存在关联主表数据\n * @param treeId 树节点ID\n * @return 是否存在主表数据\n */\n private boolean hasMainRecords(${childPkField.attrType} treeId) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n wrapper.eq(${ClassName}Entity::$childFieldGetter, treeId);\n return baseMapper.selectCount(wrapper) > 0;\n }\n\n /**\n * 规范化父节点ID\n * @param parentId 父节点ID\n * @return 规范化后的父节点ID\n */\n private ${childPkField.attrType} normalizeParentId(${childPkField.attrType} parentId) {\n return Objects.nonNull(parentId) ? parentId : ${treeRootId};\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (36,'实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (37,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (38,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (39,'子实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ChildClassName}Entity.java','子实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(\"${childTableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (40,'子MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV2026001','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (41,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (42,'api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 树形CRUD接口 ==========\n\n/**\n * 获取树形列表数据\n * @param query - 查询参数对象\n * @returns Promise<树形数据>\n */\nexport function fetchTreeList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 获取树节点详情\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<树节点详情>\n */\nexport function getTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 新增树节点\n * @param obj - 要新增的树节点对象\n * @returns Promise<操作结果>\n */\nexport function addTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 更新树节点\n * @param obj - 要更新的树节点对象\n * @returns Promise<操作结果>\n */\nexport function putTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'put\',\n data: obj\n })\n}\n\n/**\n * 删除树节点\n * @param ids - 要删除的树节点ID数组\n * @returns Promise<操作结果>\n */\nexport function delTreeObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'delete\',\n data: ids\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n *\n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (43,'表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','表格V2026001','#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n\n\n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); diff --git a/db/8pigxx_codegen.sql b/db/8pigxx_codegen.sql index 672ee8da4..5e5c35fe5 100644 --- a/db/8pigxx_codegen.sql +++ b/db/8pigxx_codegen.sql @@ -259,46 +259,46 @@ CREATE TABLE `gen_template` ( -- Records of gen_template -- ---------------------------- BEGIN; -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (1,'ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.list(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.save(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateById(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeBatchByIds(CollUtil.toList(ids)));\n }\n\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (2,'ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\n\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (3,'ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (4,'实体V2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/entity/${ClassName}Entity.java','实体V2026001','package ${package}.${moduleName}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (5,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (6,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (1,'ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.list(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.save(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateById(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeBatchByIds(CollUtil.toList(ids)));\n }\n\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (2,'ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${entityClassName};\n\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (3,'ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (4,'实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (5,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (6,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (7,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (8,'api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n\n#if($ChildClassName)\n// ========== 子表相关接口 ==========\n\n/**\n * 删除子表数据\n * @param ids - 要删除的子表数据ID数组\n * @returns Promise<操作结果>\n */\nexport function delChildObj(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/child\',\n method: \'delete\',\n data: ids\n })\n}\n#end\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (9,'表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','表格V2026001','\n\n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (10,'表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','表单V2026001','\n\n ','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (11,'主子ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','主子ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport com.github.yulichang.extension.mapping.base.MPJDeepService;\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\n\npublic interface ${ClassName}Service extends MPJDeepService<${ClassName}Entity> {\n Boolean saveDeep(${ClassName}Entity ${className});\n\n Boolean updateDeep(${ClassName}Entity ${className});\n\n Boolean removeDeep(Long[] ids);\n\n Boolean removeChild(Long[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (12,'主子ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','主子ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport org.springframework.transaction.annotation.Transactional;\nimport lombok.RequiredArgsConstructor;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveDeep(${ClassName}Entity ${className}) {\n baseMapper.insert(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert( ${childClassName});\n }\n\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateDeep(${ClassName}Entity ${className}) {\n baseMapper.updateById(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n#set($getChildPkName=$str.getProperty(${pk.attrName}))\n if (Objects.isNull(${childClassName}.$getChildPkName())) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert(${childClassName});\n } else {\n ${childClassName}Mapper.updateById(${childClassName});\n }\n }\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeDeep(Long[] ids) {\n baseMapper.deleteBatchIds(CollUtil.toList(ids));\n ${childClassName}Mapper.delete(Wrappers.<${ChildClassName}Entity>lambdaQuery().in(${ChildClassName}Entity::$str.getProperty($childField), ids));\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeChild(Long[] ids) {\n ${childClassName}Mapper.deleteBatchIds(CollUtil.toList(ids));\n return Boolean.TRUE;\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (13,'主实体V2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/entity/${ClassName}Entity.java','主实体V2026001','package ${package}.${moduleName}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\nimport org.apache.fesod.sheet.annotation.ExcelIgnore;\nimport com.github.yulichang.annotation.EntityMapping;\nimport java.util.List;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n @TableField(typeHandler = JacksonTypeHandler.class)\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n @ExcelIgnore\n @TableField(exist = false)\n @EntityMapping(thisField = \"$mainField\", joinField = \"$childField\")\n private List<${ChildClassName}Entity> ${childClassName}List;\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (14,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (15,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (11,'主子ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','主子ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport com.github.yulichang.extension.mapping.base.MPJDeepService;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\n\npublic interface ${ClassName}Service extends MPJDeepService<${ClassName}Entity> {\n Boolean saveDeep(${ClassName}Entity ${className});\n\n Boolean updateDeep(${ClassName}Entity ${className});\n\n Boolean removeDeep(Long[] ids);\n\n Boolean removeChild(Long[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (12,'主子ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','主子ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport org.springframework.transaction.annotation.Transactional;\nimport lombok.RequiredArgsConstructor;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveDeep(${ClassName}Entity ${className}) {\n baseMapper.insert(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert( ${childClassName});\n }\n\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateDeep(${ClassName}Entity ${className}) {\n baseMapper.updateById(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n#set($getChildPkName=$str.getProperty(${pk.attrName}))\n if (Objects.isNull(${childClassName}.$getChildPkName())) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert(${childClassName});\n } else {\n ${childClassName}Mapper.updateById(${childClassName});\n }\n }\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeDeep(Long[] ids) {\n baseMapper.deleteBatchIds(CollUtil.toList(ids));\n ${childClassName}Mapper.delete(Wrappers.<${ChildClassName}Entity>lambdaQuery().in(${ChildClassName}Entity::$str.getProperty($childField), ids));\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeChild(Long[] ids) {\n ${childClassName}Mapper.deleteBatchIds(CollUtil.toList(ids));\n return Boolean.TRUE;\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (13,'主实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','主实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\nimport org.apache.fesod.sheet.annotation.ExcelIgnore;\nimport com.github.yulichang.annotation.EntityMapping;\nimport java.util.List;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n @TableField(typeHandler = JacksonTypeHandler.class)\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n @ExcelIgnore\n @TableField(exist = false)\n @EntityMapping(thisField = \"$mainField\", joinField = \"$childField\")\n private List<${ChildClassName}Entity> ${childClassName}List;\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (14,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (15,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (16,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (17,'api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n\n#if($ChildClassName)\n// ========== 子表相关接口 ==========\n\n/**\n * 删除子表数据\n * @param ids - 要删除的子表数据ID数组\n * @returns Promise<操作结果>\n */\nexport function delChildObj(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/child\',\n method: \'delete\',\n data: ids\n })\n}\n#end\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (18,'子实体V2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/entity/${ChildClassName}Entity.java','子实体V2026001','package ${package}.${moduleName}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(\"${childTableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (19,'主子ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','主子ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.listDeep(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.saveDeep(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateDeep(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeDeep(ids));\n }\n\n /**\n * 通过id删除${tableComment}子表数据\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}子表数据\" , description = \"通过id删除${tableComment}子表数据\" )\n @SysLog(\"通过id删除${tableComment}子表数据\" )\n @DeleteMapping(\"/child\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeChild(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeChild(ids));\n }\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (18,'子实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ChildClassName}Entity.java','子实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(\"${childTableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (19,'主子ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','主子ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.listDeep(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.saveDeep(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateDeep(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeDeep(ids));\n }\n\n /**\n * 通过id删除${tableComment}子表数据\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}子表数据\" , description = \"通过id删除${tableComment}子表数据\" )\n @SysLog(\"通过id删除${tableComment}子表数据\" )\n @DeleteMapping(\"/child\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeChild(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeChild(ids));\n }\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (20,'主子表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','主子表单V2026001','\n\n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (21,'主子表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','主子表格V2026001','\n\n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (22,'子MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV2026001','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (23,'树形ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','树形ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 获取树形列表\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"获取树形列表\" , description = \"获取树形列表\" )\n @GetMapping(\"/tree\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Tree(@ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.buildTree(wrapper));\n }\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.list(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.save(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateById(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeBatchByIds(CollUtil.toList(ids)));\n }\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n} \n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (24,'树形ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','树形ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper);\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n boolean removeBatchByIds(List<${pk.attrType}> ids);\n} ','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (25,'树形ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','树形ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper) {\n // 查询所有数据\n List<${ClassName}Entity> allList = list(wrapper);\n \n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n // 转换为TreeNode\n List> collect = allList.stream().map(getNodeFunction()).toList();\n\n // 使用TreeUtil构建树形结构,根节点ID为0\n#if($pk.attrType == \'Long\')\n return TreeUtil.build(collect, 0L);\n#else\n return TreeUtil.build(collect, 0);\n#end\n }\n\n /**\n * 获取TreeNode转换函数\n * @return TreeNode转换函数\n */\n @NotNull\n private Function<${ClassName}Entity, TreeNode<${pk.attrType}>> getNodeFunction() {\n return entity -> {\n TreeNode<${pk.attrType}> node = new TreeNode<>();\n node.setId(entity.$str.getProperty($pk.attrName)());\n node.setName(entity.$str.getProperty($nameField)());\n node.setParentId(entity.$str.getProperty($parentField)() != null ? entity.$str.getProperty($parentField)() : 0L);\n\n // 扩展属性\n Map extra = new HashMap<>();\n#foreach($field in $fieldList)\n extra.put(${ClassName}Entity.Fields.${field.attrName}, entity.$str.getProperty($field.attrName)());\n#end\n node.setExtra(extra);\n return node;\n };\n }\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public boolean removeBatchByIds(List<${pk.attrType}> ids) {\n if (CollUtil.isEmpty(ids)) {\n return true;\n }\n\n // 获取所有要删除的ID(包括子节点)\n List<${pk.attrType}> allDeleteIds = new ArrayList<>(ids);\n \n for (${pk.attrType} id : ids) {\n List<${pk.attrType}> childIds = getChildIdsRecursive(id);\n allDeleteIds.addAll(childIds);\n }\n\n // 去重\n allDeleteIds = allDeleteIds.stream().distinct().collect(Collectors.toList());\n\n // 批量删除\n return super.removeBatchByIds(allDeleteIds);\n }\n\n /**\n * 递归获取所有子节点ID\n * @param parentId 父节点ID\n * @return 子节点ID列表\n */\n private List<${pk.attrType}> getChildIdsRecursive(${pk.attrType} parentId) {\n List<${pk.attrType}> childIds = new ArrayList<>();\n \n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n wrapper.eq(${ClassName}Entity::$str.getProperty($parentField), parentId);\n wrapper.select(${ClassName}Entity::$str.getProperty($pk.attrName));\n \n List<${ClassName}Entity> children = list(wrapper);\n \n for (${ClassName}Entity child : children) {\n ${pk.attrType} childId = child.$str.getProperty($pk.attrName)();\n childIds.add(childId);\n // 递归获取子节点的子节点\n childIds.addAll(getChildIdsRecursive(childId));\n }\n \n return childIds;\n }\n} ','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (26,'树形实体V2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/entity/${ClassName}Entity.java','树形实体V2026001','package ${package}.${moduleName}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.FieldNameConstants;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@FieldNameConstants\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (27,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (28,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (22,'子MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV2026001','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (23,'树形ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','树形ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 获取树形列表\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"获取树形列表\" , description = \"获取树形列表\" )\n @GetMapping(\"/tree\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Tree(@ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.buildTree(wrapper));\n }\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.list(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.save(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateById(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeBatchByIds(CollUtil.toList(ids)));\n }\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n} \n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (24,'树形ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','树形ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${entityClassName};\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper);\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n boolean removeBatchByIds(List<${pk.attrType}> ids);\n} ','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (25,'树形ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','树形ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper) {\n // 查询所有数据\n List<${ClassName}Entity> allList = list(wrapper);\n \n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n // 转换为TreeNode\n List> collect = allList.stream().map(getNodeFunction()).toList();\n\n // 使用TreeUtil构建树形结构,根节点ID为0\n#if($pk.attrType == \'Long\')\n return TreeUtil.build(collect, 0L);\n#else\n return TreeUtil.build(collect, 0);\n#end\n }\n\n /**\n * 获取TreeNode转换函数\n * @return TreeNode转换函数\n */\n @NotNull\n private Function<${ClassName}Entity, TreeNode<${pk.attrType}>> getNodeFunction() {\n return entity -> {\n TreeNode<${pk.attrType}> node = new TreeNode<>();\n node.setId(entity.$str.getProperty($pk.attrName)());\n node.setName(entity.$str.getProperty($nameField)());\n node.setParentId(entity.$str.getProperty($parentField)() != null ? entity.$str.getProperty($parentField)() : 0L);\n\n // 扩展属性\n Map extra = new HashMap<>();\n#foreach($field in $fieldList)\n extra.put(${ClassName}Entity.Fields.${field.attrName}, entity.$str.getProperty($field.attrName)());\n#end\n node.setExtra(extra);\n return node;\n };\n }\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public boolean removeBatchByIds(List<${pk.attrType}> ids) {\n if (CollUtil.isEmpty(ids)) {\n return true;\n }\n\n // 获取所有要删除的ID(包括子节点)\n List<${pk.attrType}> allDeleteIds = new ArrayList<>(ids);\n \n for (${pk.attrType} id : ids) {\n List<${pk.attrType}> childIds = getChildIdsRecursive(id);\n allDeleteIds.addAll(childIds);\n }\n\n // 去重\n allDeleteIds = allDeleteIds.stream().distinct().collect(Collectors.toList());\n\n // 批量删除\n return super.removeBatchByIds(allDeleteIds);\n }\n\n /**\n * 递归获取所有子节点ID\n * @param parentId 父节点ID\n * @return 子节点ID列表\n */\n private List<${pk.attrType}> getChildIdsRecursive(${pk.attrType} parentId) {\n List<${pk.attrType}> childIds = new ArrayList<>();\n \n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n wrapper.eq(${ClassName}Entity::$str.getProperty($parentField), parentId);\n wrapper.select(${ClassName}Entity::$str.getProperty($pk.attrName));\n \n List<${ClassName}Entity> children = list(wrapper);\n \n for (${ClassName}Entity child : children) {\n ${pk.attrType} childId = child.$str.getProperty($pk.attrName)();\n childIds.add(childId);\n // 递归获取子节点的子节点\n childIds.addAll(getChildIdsRecursive(childId));\n }\n \n return childIds;\n }\n} ','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (26,'树形实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','树形实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.FieldNameConstants;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@FieldNameConstants\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (27,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (28,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (29,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (30,'树形api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','树形api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 树形表格CRUD接口 ==========\n\n/**\n * 获取树形列表数据\n * @param query - 查询参数对象\n * @returns Promise<树形数据>\n */\nexport function fetchTreeList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据(递归删除子节点)\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (31,'树形表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','树形表格V2026001','\n\n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (32,'树形表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','树形表单V2026001','\n\n \n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (33,'ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','ControllerV2026001','package ${package}.${moduleName}.controller;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($childFieldGetter = $str.getProperty($childField))\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n#if($childField)\n if (Objects.nonNull(${className}.$childFieldGetter())) {\n wrapper.eq(${ClassName}Entity::$str.getProperty($childField), ${className}.$childFieldGetter());\n }\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.list(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.save(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateById(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeBatchByIds(CollUtil.toList(ids)));\n }\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n\n /**\n * 获取树形列表\n * @param ${childClassName} 树节点查询条件\n * @return 树形数据\n */\n @Operation(summary = \"获取树形列表\" , description = \"获取树形列表\" )\n @GetMapping(\"/tree\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getTree(@ParameterObject ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.buildTree(Wrappers.lambdaQuery(${childClassName})));\n }\n\n /**\n * 通过条件查询树节点\n * @param ${childClassName} 树节点查询条件\n * @return 树节点列表\n */\n @Operation(summary = \"通过条件查询树节点\" , description = \"通过条件查询树节点\" )\n @GetMapping(\"/tree/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getTreeDetails(@ParameterObject ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.getTreeDetails(${childClassName}));\n }\n\n /**\n * 新增树节点\n * @param ${childClassName} 树节点\n * @return R\n */\n @Operation(summary = \"新增树节点\" , description = \"新增树节点\" )\n @SysLog(\"新增树节点\" )\n @PostMapping(\"/tree\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R saveTree(@RequestBody ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.saveTree(${childClassName}));\n }\n\n /**\n * 修改树节点\n * @param ${childClassName} 树节点\n * @return R\n */\n @Operation(summary = \"修改树节点\" , description = \"修改树节点\" )\n @SysLog(\"修改树节点\" )\n @PutMapping(\"/tree\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateTree(@RequestBody ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.updateTree(${childClassName}));\n }\n\n /**\n * 通过id删除树节点\n * @param ids 树节点ID列表\n * @return R\n */\n @Operation(summary = \"通过id删除树节点\" , description = \"通过id删除树节点\" )\n @SysLog(\"通过id删除树节点\" )\n @DeleteMapping(\"/tree\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeTree(@RequestBody ${childPkField.attrType}[] ids) {\n return R.ok(${className}Service.removeTreeByIds(ids));\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (34,'ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper);\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName});\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n Boolean saveTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n Boolean updateTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n Boolean removeTreeByIds(${childPkField.attrType}[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (35,'ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#set($childPkGetter = $str.getProperty($childPkField.attrName))\n#break\n#end\n#end\n#set($parentGetter = $str.getProperty($parentField))\n#set($childFieldGetter = $str.getProperty($childField))\n#if($childPkField.attrType == \'Long\')\n#set($treeRootId = \'0L\')\n#elseif($childPkField.attrType == \'String\')\n#set($treeRootId = \'\"0\"\')\n#else\n#set($treeRootId = \'0\')\n#end\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper) {\n List<${ChildClassName}Entity> allList = ${childClassName}Mapper.selectList(wrapper);\n\n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n List> collect = allList.stream().map(getNodeFunction()).toList();\n return TreeUtil.build(collect, ${treeRootId});\n }\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n @Override\n public List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName}) {\n return ${childClassName}Mapper.selectList(Wrappers.query(${childClassName}));\n }\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(null, ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.insert(${childClassName}) > 0;\n }\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(${childClassName}.$str.getProperty($childPkField.attrName)(), ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.updateById(${childClassName}) > 0;\n }\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeTreeByIds(${childPkField.attrType}[] ids) {\n if (ArrayUtil.isEmpty(ids)) {\n return Boolean.TRUE;\n }\n\n for (${childPkField.attrType} id : ids) {\n if (hasChildNodes(id)) {\n throw new RuntimeException(\"当前节点存在子节点,不能删除\");\n }\n if (hasMainRecords(id)) {\n throw new RuntimeException(\"当前节点已关联主表数据,不能删除\");\n }\n }\n\n return ${childClassName}Mapper.deleteBatchIds(CollUtil.toList(ids)) > 0;\n }\n\n /**\n * 获取TreeNode转换函数\n * @return TreeNode转换函数\n */\n private Function<${ChildClassName}Entity, TreeNode<${childPkField.attrType}>> getNodeFunction() {\n return entity -> {\n TreeNode<${childPkField.attrType}> node = new TreeNode<>();\n node.setId(entity.$childPkGetter());\n node.setName(entity.$str.getProperty($nameField)());\n node.setParentId(normalizeParentId(entity.$parentGetter()));\n\n Map extra = new HashMap<>();\n#foreach($field in $childFieldList)\n extra.put(\"${field.attrName}\", entity.$str.getProperty($field.attrName)());\n#end\n node.setExtra(extra);\n return node;\n };\n }\n\n /**\n * 规范化父节点ID,并校验父子关系\n * @param currentId 当前节点ID\n * @param parentId 父节点ID\n * @return 规范化后的父节点ID\n */\n private ${childPkField.attrType} validateParentRelation(${childPkField.attrType} currentId, ${childPkField.attrType} parentId) {\n ${childPkField.attrType} normalizedParentId = normalizeParentId(parentId);\n\n if (Objects.nonNull(currentId) && Objects.equals(currentId, normalizedParentId)) {\n throw new RuntimeException(\"不能选择当前节点作为父节点\");\n }\n\n if (Objects.nonNull(currentId) && isDescendant(currentId, normalizedParentId)) {\n throw new RuntimeException(\"不能选择当前节点的后代节点作为父节点\");\n }\n\n return normalizedParentId;\n }\n\n /**\n * 判断候选父节点是否是当前节点的后代节点\n * @param currentId 当前节点ID\n * @param candidateParentId 候选父节点ID\n * @return 是否为后代节点\n */\n private boolean isDescendant(${childPkField.attrType} currentId, ${childPkField.attrType} candidateParentId) {\n if (Objects.isNull(currentId) || Objects.isNull(candidateParentId) || Objects.equals(candidateParentId, ${treeRootId})) {\n return false;\n }\n\n Set<${childPkField.attrType}> visited = new HashSet<>();\n ${ChildClassName}Entity parent = ${childClassName}Mapper.selectById(candidateParentId);\n\n while (Objects.nonNull(parent) && visited.add(parent.$childPkGetter())) {\n if (Objects.equals(currentId, parent.$childPkGetter())) {\n return true;\n }\n\n ${childPkField.attrType} nextParentId = parent.$parentGetter();\n if (Objects.isNull(nextParentId) || Objects.equals(nextParentId, ${treeRootId})) {\n return false;\n }\n parent = ${childClassName}Mapper.selectById(nextParentId);\n }\n\n return false;\n }\n\n /**\n * 判断是否存在子节点\n * @param parentId 父节点ID\n * @return 是否存在子节点\n */\n private boolean hasChildNodes(${childPkField.attrType} parentId) {\n LambdaQueryWrapper<${ChildClassName}Entity> wrapper = Wrappers.lambdaQuery();\n wrapper.eq(${ChildClassName}Entity::$parentGetter, parentId);\n return ${childClassName}Mapper.selectCount(wrapper) > 0;\n }\n\n /**\n * 判断是否存在关联主表数据\n * @param treeId 树节点ID\n * @return 是否存在主表数据\n */\n private boolean hasMainRecords(${childPkField.attrType} treeId) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n wrapper.eq(${ClassName}Entity::$childFieldGetter, treeId);\n return baseMapper.selectCount(wrapper) > 0;\n }\n\n /**\n * 规范化父节点ID\n * @param parentId 父节点ID\n * @return 规范化后的父节点ID\n */\n private ${childPkField.attrType} normalizeParentId(${childPkField.attrType} parentId) {\n return Objects.nonNull(parentId) ? parentId : ${treeRootId};\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (36,'实体V2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/entity/${ClassName}Entity.java','实体V2026001','package ${package}.${moduleName}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (37,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${package}.${moduleName}.entity.${ClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (38,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (39,'子实体V2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/entity/${ChildClassName}Entity.java','子实体V2026001','package ${package}.${moduleName}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(\"${childTableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); -INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (40,'子MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV2026001','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${package}.${moduleName}.entity.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (33,'ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','ControllerV2026001','package ${package}.${moduleName}.controller;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($childFieldGetter = $str.getProperty($childField))\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n#if($childField)\n if (Objects.nonNull(${className}.$childFieldGetter())) {\n wrapper.eq(${ClassName}Entity::$str.getProperty($childField), ${className}.$childFieldGetter());\n }\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getDetails(@ParameterObject ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.list(Wrappers.query(${className})));\n }\n\n /**\n * 新增${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"新增${tableComment}\" , description = \"新增${tableComment}\" )\n @SysLog(\"新增${tableComment}\" )\n @PostMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R save(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.save(${className}));\n }\n\n /**\n * 修改${tableComment}\n * @param ${className} ${tableComment}\n * @return R\n */\n @Operation(summary = \"修改${tableComment}\" , description = \"修改${tableComment}\" )\n @SysLog(\"修改${tableComment}\" )\n @PutMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateById(@RequestBody ${ClassName}Entity ${className}) {\n return R.ok(${className}Service.updateById(${className}));\n }\n\n /**\n * 通过id删除${tableComment}\n * @param ids ${pk.attrName}列表\n * @return R\n */\n @Operation(summary = \"通过id删除${tableComment}\" , description = \"通过id删除${tableComment}\" )\n @SysLog(\"通过id删除${tableComment}\" )\n @DeleteMapping\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeById(@RequestBody ${pk.attrType}[] ids) {\n return R.ok(${className}Service.removeBatchByIds(CollUtil.toList(ids)));\n }\n\n /**\n * 导出excel 表格\n * @param ${className} 查询条件\n * @param ids 导出指定ID\n * @return excel 文件流\n */\n @ResponseExcel\n @GetMapping(\"/export\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public List<${ClassName}Entity> exportExcel(${ClassName}Entity ${className},${pk.attrType}[] ids) {\n return ${className}Service.list(Wrappers.lambdaQuery(${className}).in(ArrayUtil.isNotEmpty(ids), ${ClassName}Entity::$str.getProperty($pk.attrName), ids));\n }\n\n /**\n * 导入excel 表\n * @param ${className}List 对象实体列表\n * @param bindingResult 错误信息列表\n * @return ok fail\n */\n @PostMapping(\"/import\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\')\" )\n #end\n public R importExcel(@RequestExcel List<${ClassName}Entity> ${className}List, BindingResult bindingResult) {\n return R.ok(${className}Service.saveBatch(${className}List));\n }\n\n /**\n * 获取树形列表\n * @param ${childClassName} 树节点查询条件\n * @return 树形数据\n */\n @Operation(summary = \"获取树形列表\" , description = \"获取树形列表\" )\n @GetMapping(\"/tree\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getTree(@ParameterObject ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.buildTree(Wrappers.lambdaQuery(${childClassName})));\n }\n\n /**\n * 通过条件查询树节点\n * @param ${childClassName} 树节点查询条件\n * @return 树节点列表\n */\n @Operation(summary = \"通过条件查询树节点\" , description = \"通过条件查询树节点\" )\n @GetMapping(\"/tree/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R getTreeDetails(@ParameterObject ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.getTreeDetails(${childClassName}));\n }\n\n /**\n * 新增树节点\n * @param ${childClassName} 树节点\n * @return R\n */\n @Operation(summary = \"新增树节点\" , description = \"新增树节点\" )\n @SysLog(\"新增树节点\" )\n @PostMapping(\"/tree\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\')\" )\n #end\n public R saveTree(@RequestBody ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.saveTree(${childClassName}));\n }\n\n /**\n * 修改树节点\n * @param ${childClassName} 树节点\n * @return R\n */\n @Operation(summary = \"修改树节点\" , description = \"修改树节点\" )\n @SysLog(\"修改树节点\" )\n @PutMapping(\"/tree\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\')\" )\n #end\n public R updateTree(@RequestBody ${ChildClassName}Entity ${childClassName}) {\n return R.ok(${className}Service.updateTree(${childClassName}));\n }\n\n /**\n * 通过id删除树节点\n * @param ids 树节点ID列表\n * @return R\n */\n @Operation(summary = \"通过id删除树节点\" , description = \"通过id删除树节点\" )\n @SysLog(\"通过id删除树节点\" )\n @DeleteMapping(\"/tree\")\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\')\" )\n #end\n public R removeTree(@RequestBody ${childPkField.attrType}[] ids) {\n return R.ok(${className}Service.removeTreeByIds(ids));\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (34,'ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper);\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName});\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n Boolean saveTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n Boolean updateTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n Boolean removeTreeByIds(${childPkField.attrType}[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (35,'ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#set($childPkGetter = $str.getProperty($childPkField.attrName))\n#break\n#end\n#end\n#set($parentGetter = $str.getProperty($parentField))\n#set($childFieldGetter = $str.getProperty($childField))\n#if($childPkField.attrType == \'Long\')\n#set($treeRootId = \'0L\')\n#elseif($childPkField.attrType == \'String\')\n#set($treeRootId = \'\"0\"\')\n#else\n#set($treeRootId = \'0\')\n#end\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper) {\n List<${ChildClassName}Entity> allList = ${childClassName}Mapper.selectList(wrapper);\n\n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n List> collect = allList.stream().map(getNodeFunction()).toList();\n return TreeUtil.build(collect, ${treeRootId});\n }\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n @Override\n public List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName}) {\n return ${childClassName}Mapper.selectList(Wrappers.query(${childClassName}));\n }\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(null, ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.insert(${childClassName}) > 0;\n }\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(${childClassName}.$str.getProperty($childPkField.attrName)(), ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.updateById(${childClassName}) > 0;\n }\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeTreeByIds(${childPkField.attrType}[] ids) {\n if (ArrayUtil.isEmpty(ids)) {\n return Boolean.TRUE;\n }\n\n for (${childPkField.attrType} id : ids) {\n if (hasChildNodes(id)) {\n throw new RuntimeException(\"当前节点存在子节点,不能删除\");\n }\n if (hasMainRecords(id)) {\n throw new RuntimeException(\"当前节点已关联主表数据,不能删除\");\n }\n }\n\n return ${childClassName}Mapper.deleteBatchIds(CollUtil.toList(ids)) > 0;\n }\n\n /**\n * 获取TreeNode转换函数\n * @return TreeNode转换函数\n */\n private Function<${ChildClassName}Entity, TreeNode<${childPkField.attrType}>> getNodeFunction() {\n return entity -> {\n TreeNode<${childPkField.attrType}> node = new TreeNode<>();\n node.setId(entity.$childPkGetter());\n node.setName(entity.$str.getProperty($nameField)());\n node.setParentId(normalizeParentId(entity.$parentGetter()));\n\n Map extra = new HashMap<>();\n#foreach($field in $childFieldList)\n extra.put(\"${field.attrName}\", entity.$str.getProperty($field.attrName)());\n#end\n node.setExtra(extra);\n return node;\n };\n }\n\n /**\n * 规范化父节点ID,并校验父子关系\n * @param currentId 当前节点ID\n * @param parentId 父节点ID\n * @return 规范化后的父节点ID\n */\n private ${childPkField.attrType} validateParentRelation(${childPkField.attrType} currentId, ${childPkField.attrType} parentId) {\n ${childPkField.attrType} normalizedParentId = normalizeParentId(parentId);\n\n if (Objects.nonNull(currentId) && Objects.equals(currentId, normalizedParentId)) {\n throw new RuntimeException(\"不能选择当前节点作为父节点\");\n }\n\n if (Objects.nonNull(currentId) && isDescendant(currentId, normalizedParentId)) {\n throw new RuntimeException(\"不能选择当前节点的后代节点作为父节点\");\n }\n\n return normalizedParentId;\n }\n\n /**\n * 判断候选父节点是否是当前节点的后代节点\n * @param currentId 当前节点ID\n * @param candidateParentId 候选父节点ID\n * @return 是否为后代节点\n */\n private boolean isDescendant(${childPkField.attrType} currentId, ${childPkField.attrType} candidateParentId) {\n if (Objects.isNull(currentId) || Objects.isNull(candidateParentId) || Objects.equals(candidateParentId, ${treeRootId})) {\n return false;\n }\n\n Set<${childPkField.attrType}> visited = new HashSet<>();\n ${ChildClassName}Entity parent = ${childClassName}Mapper.selectById(candidateParentId);\n\n while (Objects.nonNull(parent) && visited.add(parent.$childPkGetter())) {\n if (Objects.equals(currentId, parent.$childPkGetter())) {\n return true;\n }\n\n ${childPkField.attrType} nextParentId = parent.$parentGetter();\n if (Objects.isNull(nextParentId) || Objects.equals(nextParentId, ${treeRootId})) {\n return false;\n }\n parent = ${childClassName}Mapper.selectById(nextParentId);\n }\n\n return false;\n }\n\n /**\n * 判断是否存在子节点\n * @param parentId 父节点ID\n * @return 是否存在子节点\n */\n private boolean hasChildNodes(${childPkField.attrType} parentId) {\n LambdaQueryWrapper<${ChildClassName}Entity> wrapper = Wrappers.lambdaQuery();\n wrapper.eq(${ChildClassName}Entity::$parentGetter, parentId);\n return ${childClassName}Mapper.selectCount(wrapper) > 0;\n }\n\n /**\n * 判断是否存在关联主表数据\n * @param treeId 树节点ID\n * @return 是否存在主表数据\n */\n private boolean hasMainRecords(${childPkField.attrType} treeId) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n wrapper.eq(${ClassName}Entity::$childFieldGetter, treeId);\n return baseMapper.selectCount(wrapper) > 0;\n }\n\n /**\n * 规范化父节点ID\n * @param parentId 父节点ID\n * @return 规范化后的父节点ID\n */\n private ${childPkField.attrType} normalizeParentId(${childPkField.attrType} parentId) {\n return Objects.nonNull(parentId) ? parentId : ${treeRootId};\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (36,'实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (37,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (38,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','\n\n\n\n\n \n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n \n #else\n \n #end\n#end\n \n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (39,'子实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ChildClassName}Entity.java','子实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(\"${childTableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); +INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (40,'子MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV2026001','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (41,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (42,'api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 树形CRUD接口 ==========\n\n/**\n * 获取树形列表数据\n * @param query - 查询参数对象\n * @returns Promise<树形数据>\n */\nexport function fetchTreeList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 获取树节点详情\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<树节点详情>\n */\nexport function getTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 新增树节点\n * @param obj - 要新增的树节点对象\n * @returns Promise<操作结果>\n */\nexport function addTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 更新树节点\n * @param obj - 要更新的树节点对象\n * @returns Promise<操作结果>\n */\nexport function putTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'put\',\n data: obj\n })\n}\n\n/**\n * 删除树节点\n * @param ids - 要删除的树节点ID数组\n * @returns Promise<操作结果>\n */\nexport function delTreeObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'delete\',\n data: ids\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n *\n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (43,'表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','表格V2026001','#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n\n\n\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin'); diff --git a/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/service/impl/GeneratorServiceImpl.java b/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/service/impl/GeneratorServiceImpl.java index 2711daed4..358363eee 100755 --- a/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/service/impl/GeneratorServiceImpl.java +++ b/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/service/impl/GeneratorServiceImpl.java @@ -52,6 +52,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringBootVersion; import org.springframework.stereotype.Service; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.*; import java.util.stream.Collectors; import java.util.zip.ZipEntry; @@ -449,6 +451,7 @@ public class GeneratorServiceImpl implements GeneratorService { dataModel.put(DataModelConstants.BACKEND_PATH, table.getBackendPath()); dataModel.put(DataModelConstants.FRONTEND_PATH, table.getFrontendPath()); + setEntityModel(dataModel, table); // 设置子表 String childTableName = table.getChildTableName(); @@ -488,12 +491,16 @@ public class GeneratorServiceImpl implements GeneratorService { } /** - * 判断当前是否是 SpringBoot3 版本 + * 判断当前是否为 SpringBoot 3 及以上版本(含 SpringBoot 4)。 + *

+ * 模板变量 {@code $isSpringBoot3} 沿用历史命名未改,实际语义是「是否使用 + * SpringBoot 3 之后的注解和包路径」,目前覆盖 SpringBoot 3.x 与 4.x。新引入 + * 更高主版本时,请在此处补齐前缀,避免模板渲染走到 SpringBoot 2 分支。 * - * @return true/fasle + * @return SpringBoot 主版本 >= 3 返回 true */ private boolean isSpringBoot3() { - return StrUtil.startWith(SpringBootVersion.getVersion(), "3"); + return StrUtil.startWithAny(SpringBootVersion.getVersion(), "3", "4"); } /** @@ -506,6 +513,101 @@ public class GeneratorServiceImpl implements GeneratorService { return StrUtil.removeAll(table.getModuleName(), StrUtil.DASHED); } + /** + * 设置实体生成路径和实体包名。 + *

+ * 默认与业务模块同包,实体落到 {@code backendPath} 自身(即 biz 模块)。当 + * {@code backendPath} 末尾形如 {@code xxx-biz} 且同级目录存在 {@code xxx-api} + * 模块时,自动切换为:实体写入 {@code xxx-api},包名调整为 + * {@code ..api.entity},便于在多个 biz 模块之间共享实体 + * 而不引入循环依赖。 + * + * @param dataModel 数据模型,写入 entityPath、entityPackage、entityPackagePath、 + * entityClassName、apiPath、hasApiModule + * @param table 表配置 + */ + private void setEntityModel(Map dataModel, GenTable table) { + String modulePackage = StrUtil.format("{}.{}", table.getPackageName(), normalizedModuleName(table)); + String entityPackage = StrUtil.format("{}.entity", modulePackage); + String entityPath = normalizePathSeparator(table.getBackendPath()); + String apiPath = StrUtil.EMPTY; + boolean hasApiModule = false; + + Optional resolvedApiPath = resolveApiPath(table.getBackendPath()); + if (resolvedApiPath.isPresent()) { + apiPath = resolvedApiPath.get(); + entityPath = apiPath; + entityPackage = StrUtil.format("{}.api.entity", modulePackage); + hasApiModule = true; + } + + dataModel.put(DataModelConstants.HAS_API_MODULE, hasApiModule); + dataModel.put(DataModelConstants.API_PATH, apiPath); + dataModel.put(DataModelConstants.ENTITY_PATH, entityPath); + dataModel.put(DataModelConstants.ENTITY_PACKAGE, entityPackage); + dataModel.put(DataModelConstants.ENTITY_PACKAGE_PATH, entityPackage.replace(StrUtil.DOT, StrUtil.SLASH)); + dataModel.put(DataModelConstants.ENTITY_CLASS_NAME, + StrUtil.format("{}.{}Entity", entityPackage, table.getClassName())); + } + + /** + * 通过 biz 模块路径推断同级 api 模块路径。 + *

+ * 仅当末尾目录形如 {@code xxx-biz} 且同级 {@code xxx-api} 目录真实存在时 + * 才返回 api 模块路径,否则返回空 {@link Optional},调用方继续沿用 biz + * 同包实体生成策略。返回值已统一为 {@code /} 分隔符,避免 Windows 下与 + * 模板拼接产生混合分隔符。 + * + * @param backendPath 后端生成路径(biz 模块的绝对或相对路径) + * @return 同级 api 模块路径;推断失败时返回 {@link Optional#empty()} + */ + private Optional resolveApiPath(String backendPath) { + if (StrUtil.isBlank(backendPath)) { + return Optional.empty(); + } + + Path bizPath = Path.of(normalizePathSeparator(backendPath)).normalize(); + Path bizFileName = bizPath.getFileName(); + if (Objects.isNull(bizFileName)) { + return Optional.empty(); + } + + String bizDirectory = bizFileName.toString(); + if (!StrUtil.endWith(bizDirectory, "-biz")) { + return Optional.empty(); + } + + Path parent = bizPath.getParent(); + if (Objects.isNull(parent)) { + return Optional.empty(); + } + + Path apiPath = parent.resolve(StrUtil.removeSuffix(bizDirectory, "-biz") + "-api").normalize(); + if (!Files.isDirectory(apiPath)) { + return Optional.empty(); + } + + return Optional.of(normalizePathSeparator(apiPath.toString())); + } + + /** + * 归一化路径分隔符。 + *

+ * 借助 {@link FileUtil#normalize(String)} 将反斜杠统一替换为 {@code /} 并清理 + * 冗余 {@code ..} 与重复分隔符,确保后续 {@code Path.of} 解析以及模板字符串 + * 拼接(如 {@code ${entityPath}/src/main/java/...})在 Windows 与类 Unix 平台 + * 上得到一致的路径形态。 + * + * @param path 原始路径 + * @return 归一化后的路径;空入参返回空字符串 + */ + private String normalizePathSeparator(String path) { + if (StrUtil.isBlank(path)) { + return StrUtil.EMPTY; + } + return FileUtil.normalize(path); + } + /** * 将表字段按照类型分组并存储到数据模型中 * diff --git a/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/util/DataModelConstants.java b/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/util/DataModelConstants.java index cf331dd4f..f1a32ba82 100644 --- a/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/util/DataModelConstants.java +++ b/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/util/DataModelConstants.java @@ -61,6 +61,18 @@ public class DataModelConstants { public static final String FRONTEND_PATH = "frontendPath"; + public static final String API_PATH = "apiPath"; + + public static final String ENTITY_PATH = "entityPath"; + + public static final String ENTITY_PACKAGE = "entityPackage"; + + public static final String ENTITY_PACKAGE_PATH = "entityPackagePath"; + + public static final String ENTITY_CLASS_NAME = "entityClassName"; + + public static final String HAS_API_MODULE = "hasApiModule"; + // 子表信息 public static final String CHILD_FIELD_LIST = "childFieldList";