diff --git a/doc/Ruoyi-Flex-Guide.docx b/doc/Ruoyi-Flex-Guide.docx index 068631e..928fc45 100644 Binary files a/doc/Ruoyi-Flex-Guide.docx and b/doc/Ruoyi-Flex-Guide.docx differ diff --git a/ruoyi-extra/ruoyi-easyretry-server/src/main/resources/logback-plus.xml b/ruoyi-extra/ruoyi-easyretry-server/src/main/resources/logback-plus.xml index cdd5451..bcd3abc 100644 --- a/ruoyi-extra/ruoyi-easyretry-server/src/main/resources/logback-plus.xml +++ b/ruoyi-extra/ruoyi-easyretry-server/src/main/resources/logback-plus.xml @@ -36,7 +36,7 @@ ${log.path}/info.log - ${log.base}/info.%d{yyyy-MM-dd}.log + ${log.path}/info.%d{yyyy-MM-dd}.log 60 @@ -52,7 +52,7 @@ ${log.path}/error.log - ${log.base}/error.%d{yyyy-MM-dd}.log + ${log.path}/error.%d{yyyy-MM-dd}.log 60 diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java index 90b085b..a3a6cd7 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java @@ -136,11 +136,13 @@ public class VelocityUtils { List templates = new ArrayList<>(); templates.add("vm/java/domain.java.vm"); templates.add("vm/java/vo.java.vm"); + templates.add("vm/java/vo-import.java.vm"); templates.add("vm/java/bo.java.vm"); templates.add("vm/java/mapper.java.vm"); templates.add("vm/java/service.java.vm"); templates.add("vm/java/serviceImpl.java.vm"); templates.add("vm/java/controller.java.vm"); + templates.add("vm/java/listener.java.vm"); templates.add("vm/xml/mapper.xml.vm"); if (DataBaseHelper.isPostgreSql()) { templates.add("vm/sql/postgresql/sql.vm"); @@ -225,6 +227,7 @@ public class VelocityUtils { case "vm/java/sub-domain.java.vm" -> fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName()); case "vm/java/vo.java.vm" -> fileName = StringUtils.format("{}/domain/vo/{}Vo.java", javaPath, className); + case "vm/java/vo-import.java.vm" -> fileName = StringUtils.format("{}/domain/vo/{}ImportVo.java", javaPath, className); case "vm/java/bo.java.vm" -> fileName = StringUtils.format("{}/domain/bo/{}Bo.java", javaPath, className); case "vm/java/mapper.java.vm" -> fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); @@ -236,6 +239,8 @@ public class VelocityUtils { fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); case "vm/java/controller.java.vm" -> fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className); + case "vm/java/listener.java.vm" -> + fileName = StringUtils.format("{}/listener/{}ImportListener.java", javaPath, className); case "vm/xml/mapper.xml.vm" -> fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className); case "vm/xml/sub-mapper.xml.vm" -> fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, genTable.getSubTable().getClassName()); diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm index 5f7a568..7db75d6 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm @@ -1,5 +1,6 @@ package ${packageName}.controller; +import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; import jakarta.servlet.http.HttpServletResponse; @@ -7,6 +8,7 @@ import jakarta.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; +import com.ruoyi.common.excel.core.ExcelResult; import com.ruoyi.common.core.core.domain.R; import com.ruoyi.common.excel.utils.ExcelUtil; import com.ruoyi.common.log.annotation.Log; @@ -15,8 +17,11 @@ import com.ruoyi.common.web.annotation.RepeatSubmit; import com.ruoyi.common.web.core.BaseController; import jakarta.annotation.Resource; import ${packageName}.domain.vo.${ClassName}Vo; +import ${packageName}.domain.vo.${ClassName}ImportVo; import ${packageName}.domain.bo.${ClassName}Bo; +import ${packageName}.listener.${ClassName}ImportListener; import ${packageName}.service.I${ClassName}Service; +import org.springframework.web.multipart.MultipartFile; #if($table.crud || $table.sub) import com.ruoyi.common.orm.core.page.TableDataInfo; @@ -68,6 +73,26 @@ public class ${ClassName}Controller extends BaseController ExcelUtil.exportExcel(list, "${functionName}", ${ClassName}Vo.class, response); } + /** + * 导入数据 + * + * @param file 导入文件 + * @param updateSupport 是否更新已存在数据 + */ + @Log(title = "${functionName}", businessType = BusinessType.IMPORT) + @SaCheckPermission("${permissionPrefix}:import") + @PostMapping("/importData") + public R importData(MultipartFile file, boolean updateSupport) throws Exception { + ExcelResult<${ClassName}ImportVo> result = ExcelUtil.importExcel(file.getInputStream(), ${ClassName}ImportVo.class, new ${ClassName}ImportListener(updateSupport)); + return R.ok(result.getAnalysis()); + } + + @SaCheckPermission("${permissionPrefix}:import") + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) { + ExcelUtil.exportExcel(new ArrayList<>(), "${functionName}", ${ClassName}ImportVo.class, response); + } + /** * 获取${functionName}详细信息 */ diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/listener.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/listener.java.vm new file mode 100644 index 0000000..0c16aa9 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/listener.java.vm @@ -0,0 +1,126 @@ +package ${packageName}.listener; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.ruoyi.common.core.exception.ServiceException; +import com.ruoyi.common.core.utils.SpringUtils; +import com.ruoyi.common.core.utils.ValidatorUtils; +import com.ruoyi.common.excel.core.ExcelListener; +import com.ruoyi.common.excel.core.ExcelResult; +import ${packageName}.domain.bo.${ClassName}Bo; +import ${packageName}.domain.vo.*; +import ${packageName}.service.*; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +/** + * ${functionName}自定义导入 + * + * @author ${author} + */ +@Slf4j +public class ${ClassName}ImportListener extends AnalysisEventListener<${ClassName}ImportVo> implements ExcelListener<${ClassName}ImportVo> { + private final I${ClassName}Service ${className}Service; + + private final Boolean isUpdateSupport; + private int successNum = 0; + private int failureNum = 0; + private final StringBuilder successMsg = new StringBuilder(); + private final StringBuilder failureMsg = new StringBuilder(); + + public ${ClassName}ImportListener(Boolean isUpdateSupport) { + this.${className}Service = SpringUtils.getBean(I${ClassName}Service.class); + this.isUpdateSupport = isUpdateSupport; + } + + @Override + public void invoke(${ClassName}ImportVo ${className}Vo, AnalysisContext context) { + try { + + ${ClassName}Bo ${className}Bo = BeanUtil.toBean(${className}Vo, ${ClassName}Bo.class); + + //TODO:根据某个字段,查询数据库表中是否存在记录,不存在就新增,存在就更新 + ${ClassName}Vo ${className}Vo1 = null; + + #if($table.tree) + ${className}Vo1 = ${className}Service.selectById(${className}Vo.get${pkColumn.capJavaField}()); + #else + //${className}Vo1 = ${className}Service.selectBySomefield(${className}Vo.getSomefield()); + #end + if (ObjectUtil.isNull(${className}Vo1)) { + //不存在就新增 + ${className}Bo.setVersion(0); + ValidatorUtils.validate(${className}Bo); + #if($table.tree) + boolean inserted = ${className}Service.insertWithPk(${className}Bo);//树表需要前台传来主键值 + #else + boolean inserted = ${className}Service.insert(${className}Bo); + #end + + if (inserted) { + successNum++; + successMsg.append("
").append(successNum).append("、${functionName} 记录导入成功"); + return; + } else { + failureNum++; + failureMsg.append("
").append(failureNum).append("、${functionName} 记录导入失败"); + return; + } + } else if (isUpdateSupport) { + //存在就更新 + ${className}Bo.set${pkColumn.capJavaField}(${className}Vo1.get${pkColumn.capJavaField}());//主键 + ${className}Bo.setVersion(${className}Vo1.getVersion()); + boolean updated = ${className}Service.update(${className}Bo); + if (updated) { + successNum++; + successMsg.append("
").append(successNum).append("、${functionName} 记录更新成功"); + return; + } else { + failureNum++; + failureMsg.append("
").append(failureNum).append("、${functionName} 记录更新失败"); + return; + } + } + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、${functionName} 记录导入失败:"; + failureMsg.append(msg).append(e.getMessage()); + log.error(msg, e); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + + } + + @Override + public ExcelResult<${ClassName}ImportVo> getExcelResult() { + return new ExcelResult<>() { + + @Override + public String getAnalysis() { + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据没有成功导入,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + @Override + public List<${ClassName}ImportVo> getList() { + return null; + } + + @Override + public List getErrorList() { + return null; + } + }; + } +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm index 72ba8eb..8023bb5 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm @@ -51,6 +51,14 @@ public interface I${ClassName}Service extends IBaseService<${ClassName}> */ boolean insert(${ClassName}Bo ${className}Bo); + /** + * 新增${functionName},前台提供主键值,一般用于导入的场合 + * + * @param ${className}Bo ${functionName}Bo + * @return 结果:true 操作成功,false 操作失败 + */ + boolean insertWithPk(${ClassName}Bo ${className}Bo); + /** * 修改${functionName} * diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm index 5be2a76..5dd1937 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -5,7 +5,9 @@ import java.util.List; import java.util.Map; import cn.hutool.core.util.ObjectUtil; import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryMethods; import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.update.UpdateChain; import com.ruoyi.common.core.utils.MapstructUtils; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.orm.core.page.PageQuery; @@ -149,6 +151,21 @@ public class ${ClassName}ServiceImpl extends BaseServiceImpl<${ClassName}Mapper, { ${ClassName} ${className} = MapstructUtils.convert(${className}Bo, ${ClassName}.class); +#if($table.tree) + //获取祖级列表字段 + Long parentId = ${className}.getParentId(); + if (parentId == 0) { + ${className}.setAncestors("0"); + } else { + ${ClassName}Vo parent${ClassName} = selectById(${className}Bo.getParentId()); + if (ObjectUtil.isNotNull(parent${ClassName})) { + ${className}.setAncestors(parent${ClassName}.getAncestors()+"," +parentId); + } else { + ${className}.setAncestors("0"); + } + } +#end + #if($table.sub) boolean inserted = this.save(${className});//使用全局配置的雪花算法主键生成器生成ID值 if (inserted && ObjectUtil.isNotNull(${className})) { @@ -160,6 +177,46 @@ public class ${ClassName}ServiceImpl extends BaseServiceImpl<${ClassName}Mapper, #end } + /** + * 新增${functionName},前台提供主键值,一般用于导入的场合 + * + * @param ${className}Bo ${functionName}Bo + * @return 结果:true 操作成功,false 操作失败 + */ + #if($table.sub) + @Transactional + #end + @Override + public boolean insertWithPk(${ClassName}Bo ${className}Bo) + { + ${ClassName} ${className} = MapstructUtils.convert(${className}Bo, ${ClassName}.class); + + #if($table.tree) + //获取祖级列表字段 + Long parentId = ${className}.getParentId(); + if (parentId == 0) { + ${className}.setAncestors("0"); + } else { + ${ClassName}Vo parent${ClassName} = selectById(${className}Bo.getParentId()); + if (ObjectUtil.isNotNull(parent${ClassName})) { + ${className}.setAncestors(parent${ClassName}.getAncestors()+"," +parentId); + } else { + ${className}.setAncestors("0"); + } + } + #end + + #if($table.sub) + boolean inserted = ${className}Mapper.insertWithPk(${className}) > 0;//前台传来主键值 + if (inserted && ObjectUtil.isNotNull(${className})) { + return insert${subClassName}(${className}); + } + return false; + #else + return ${className}Mapper.insertWithPk(${className}) > 0;//前台传来主键值 + #end + } + /** * 修改${functionName} * @@ -174,6 +231,17 @@ public class ${ClassName}ServiceImpl extends BaseServiceImpl<${ClassName}Mapper, { ${ClassName} ${className} = MapstructUtils.convert(${className}Bo, ${ClassName}.class); if(ObjectUtil.isNotNull(${className}) && ObjectUtil.isNotNull(${className}.get${pkColumn.capJavaField}())) { + #if($table.tree) + //更新祖级列表字段 + ${ClassName}Vo newParent${ClassName} = selectById(${className}.getParentId()); + ${ClassName}Vo old${ClassName} = selectById(${className}.get${pkColumn.capJavaField}()); + if ( ObjectUtil.isNotNull(newParent${ClassName}) && ObjectUtil.isNotNull(old${ClassName}) ) { + String newAncestors = newParent${ClassName}.getAncestors() + "," + newParent${ClassName}.get${pkColumn.capJavaField}(); + String oldAncestors = old${ClassName}.getAncestors(); + ${className}.setAncestors(newAncestors); + update${ClassName}Children(${className}.get${pkColumn.capJavaField}(), newAncestors, oldAncestors); + } + #end boolean updated = this.updateById(${className}); #if($table.sub) if (updated) { @@ -188,6 +256,33 @@ public class ${ClassName}ServiceImpl extends BaseServiceImpl<${ClassName}Mapper, return false; } + #if($table.tree) + /** + * 修改子元素关系 + * + * @param ${pkColumn.javaField} 主键ID + * @param newAncestors 新的父ID集合 + * @param oldAncestors 旧的父ID集合 + */ + @Transactional + public void update${ClassName}Children(Long ${pkColumn.javaField}, String newAncestors, String oldAncestors) { + QueryWrapper queryWrapper = QueryWrapper.create() + .from(${CapitalUnderScoreClassName}) + .where(QueryMethods.findInSet(QueryMethods.number(${pkColumn.javaField}), ${CapitalUnderScoreClassName}.ANCESTORS).gt(0)); + + List<${ClassName}Vo> children = this.listAs(queryWrapper, ${ClassName}Vo.class); + + for (${ClassName}Vo child : children) { + child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); + + UpdateChain.of(${ClassName}.class) + .set(${ClassName}::getAncestors, child.getAncestors()) + .where(${ClassName}::get${pkColumn.capJavaField}).eq(child.get${pkColumn.capJavaField}()) + .update(); + } + } + #end + /** * 批量删除${functionName} * diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo-import.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo-import.java.vm new file mode 100644 index 0000000..afee2d3 --- /dev/null +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo-import.java.vm @@ -0,0 +1,70 @@ +package ${packageName}.domain.vo; + +#foreach ($import in $importList) +import ${import}; +#end +import com.alibaba.excel.annotation.ExcelProperty; +import com.ruoyi.common.excel.annotation.ExcelDictFormat; +import com.ruoyi.common.excel.convert.ExcelDictConvert; +import lombok.Data; +import java.io.Serial; +import java.io.Serializable; +import lombok.NoArgsConstructor; + +/** + * ${functionName}导入视图对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ + +@Data +@NoArgsConstructor +public class ${ClassName}ImportVo implements Serializable +{ + + @Serial + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) + #if($column.isPk=='1') + #if($table.tree) + /** $column.columnComment */ + @ExcelProperty(value = "${column.columnComment}") + private $column.javaType $column.javaField; + #end + #end +#end + + #foreach ($column in $columns) +#if($column.isPk!='1') +#if(!$table.isSuperColumn($column.javaField) || ($column.javaField.equals("parentId")) || ($column.javaField.equals("orderNum"))) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if(${column.dictType} && ${column.dictType} != '') + @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "${column.dictType}") +#elseif($parentheseIndex != -1) + @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "$column.readConverterExp()") +#else + @ExcelProperty(value = "${comment}") +#end + private $column.javaType $column.javaField; + +#else + @ExcelProperty(value = "${column.columnComment}") + private $column.javaType $column.javaField; + +#end +#end +#end +#end + +} diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/element.ts.index-tree.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/element.ts.index-tree.vue.vm index d460877..d2495c7 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/element.ts.index-tree.vue.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/element.ts.index-tree.vue.vm @@ -73,7 +73,10 @@ 新增 - 导出 + 导入 + + + 导出 展开/折叠 @@ -271,12 +274,50 @@ + + + + + + + +
将文件拖到此处,或点击上传
+ +
+ +
-