columns)
+ {
+ for (GenTableColumn column : columns)
+ {
+ // 非超级字段、有字典类型、且前端控件为选择类时添加
+ if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny(
+ column.getHtmlType(),
+ new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX }))
+ {
+ dicts.add("'" + column.getDictType() + "'");
+ }
+ }
+ }
+
+ /**
+ * 生成权限前缀
+ * 格式:模块名:业务名(用于权限控制)
+ *
+ * @param moduleName 模块名
+ * @param businessName 业务名
+ * @return 权限前缀字符串
+ */
+ public static String getPermissionPrefix(String moduleName, String businessName)
+ {
+ return StringUtils.format("{}:{}", moduleName, businessName);
+ }
+
+ /**
+ * 获取上级菜单ID
+ * 从配置中提取,无配置则使用默认值
+ *
+ * @param paramsObj 配置参数对象
+ * @return 上级菜单ID
+ */
+ public static String getParentMenuId(JSONObject paramsObj)
+ {
+ if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID)
+ && StringUtils.isNotEmpty(paramsObj.getString(GenConstants.PARENT_MENU_ID)))
+ {
+ return paramsObj.getString(GenConstants.PARENT_MENU_ID);
+ }
+ return DEFAULT_PARENT_MENU_ID;
+ }
+
+ /**
+ * 获取树编码字段(Java属性名)
+ * 从配置中提取并转换为驼峰命名
+ *
+ * @param paramsObj 配置参数对象
+ * @return 树编码字段名(驼峰)
+ */
+ public static String getTreecode(JSONObject paramsObj)
+ {
+ if (paramsObj.containsKey(GenConstants.TREE_CODE))
+ {
+ return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE));
+ }
+ return StringUtils.EMPTY;
+ }
+
+ /**
+ * 获取树父编码字段(Java属性名)
+ * 从配置中提取并转换为驼峰命名
+ *
+ * @param paramsObj 配置参数对象
+ * @return 树父编码字段名(驼峰)
+ */
+ public static String getTreeParentCode(JSONObject paramsObj)
+ {
+ if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE))
+ {
+ return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE));
+ }
+ return StringUtils.EMPTY;
+ }
+
+ /**
+ * 获取树名称字段(Java属性名)
+ * 从配置中提取并转换为驼峰命名
+ *
+ * @param paramsObj 配置参数对象
+ * @return 树名称字段名(驼峰)
+ */
+ public static String getTreeName(JSONObject paramsObj)
+ {
+ if (paramsObj.containsKey(GenConstants.TREE_NAME))
+ {
+ return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME));
+ }
+ return StringUtils.EMPTY;
+ }
+
+ /**
+ * 计算树表展开按钮所在列的序号
+ * 用于前端树表渲染时确定在哪一列显示展开/折叠按钮
+ *
+ * @param genTable 业务表信息
+ * @return 展开按钮列的序号(从1开始)
+ */
+ public static int getExpandColumn(GenTable genTable)
+ {
+ String options = genTable.getOptions();
+ JSONObject paramsObj = JSON.parseObject(options);
+ String treeName = paramsObj.getString(GenConstants.TREE_NAME);
+ int num = 0;
+
+ // 遍历列表字段,找到树名称字段所在的列序号
+ for (GenTableColumn column : genTable.getColumns())
+ {
+ if (column.isList())
+ {
+ num++;
+ String columnName = column.getColumnName();
+ if (columnName.equals(treeName))
+ {
+ break;
+ }
+ }
+ }
+ return num;
+ }
+}
\ No newline at end of file
diff --git a/huacai-generator/src/main/resources/generator.yml b/huacai-generator/src/main/resources/generator.yml
new file mode 100644
index 0000000..5eb98d3
--- /dev/null
+++ b/huacai-generator/src/main/resources/generator.yml
@@ -0,0 +1,10 @@
+# 代码生成
+gen:
+ # 作者
+ author: huacai
+ # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
+ packageName: com.huacai
+ # 自动去除表前缀,默认是false
+ autoRemovePre: false
+ # 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
+ tablePrefix: ylxt_
diff --git a/huacai-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/huacai-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml
new file mode 100644
index 0000000..1e3746b
--- /dev/null
+++ b/huacai-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml
@@ -0,0 +1,182 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ select
+ column_id, table_id, column_name, column_comment, column_type,
+ java_type, java_field, is_pk, is_increment, is_required,
+ is_insert, is_edit, is_list, is_query, query_type, html_type,
+ dict_type, sort, create_by, create_time, update_by, update_time
+ from gen_table_column
+
+
+
+
+
+
+
+
+
+
+ insert into gen_table_column (
+ table_id,
+ column_name,
+ column_comment,
+ column_type,
+ java_type,
+ java_field,
+ is_pk,
+ is_increment,
+ is_required,
+ is_insert,
+ is_edit,
+ is_list,
+ is_query,
+ query_type,
+ html_type,
+ dict_type,
+ sort,
+ create_by,
+ create_time
+ )values(
+ #{tableId},
+ #{columnName},
+ #{columnComment},
+ #{columnType},
+ #{javaType},
+ #{javaField},
+ #{isPk},
+ #{isIncrement},
+ #{isRequired},
+ #{isInsert},
+ #{isEdit},
+ #{isList},
+ #{isQuery},
+ #{queryType},
+ #{htmlType},
+ #{dictType},
+ #{sort},
+ #{createBy},
+ sysdate() -- 创建时间为当前系统时间
+ )
+
+
+
+
+ update gen_table_column
+
+ column_comment = #{columnComment},
+ java_type = #{javaType},
+ java_field = #{javaField},
+ is_insert = #{isInsert},
+ is_edit = #{isEdit},
+ is_list = #{isList},
+ is_query = #{isQuery},
+ is_required = #{isRequired},
+ query_type = #{queryType},
+ html_type = #{htmlType},
+ dict_type = #{dictType},
+ sort = #{sort},
+ update_by = #{updateBy},
+ update_time = sysdate() -- 更新时间为当前系统时间
+
+ where column_id = #{columnId} -- 按主键更新
+
+
+
+
+ delete from gen_table_column where table_id in
+
+ #{tableId}
+
+
+
+
+
+ delete from gen_table_column where column_id in
+
+ #{item.columnId}
+
+
+
+
\ No newline at end of file
diff --git a/huacai-generator/src/main/resources/mapper/generator/GenTableMapper.xml b/huacai-generator/src/main/resources/mapper/generator/GenTableMapper.xml
new file mode 100644
index 0000000..f5b0233
--- /dev/null
+++ b/huacai-generator/src/main/resources/mapper/generator/GenTableMapper.xml
@@ -0,0 +1,304 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ select
+ table_id, table_name, table_comment, sub_table_name, sub_table_fk_name,
+ class_name, tpl_category, tpl_web_type, package_name, module_name,
+ business_name, function_name, function_author, gen_type, gen_path,
+ options, create_by, create_time, update_by, update_time, remark
+ from gen_table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ insert into gen_table (
+ table_name,
+ table_comment,
+ class_name,
+ tpl_category,
+ tpl_web_type,
+ package_name,
+ module_name,
+ business_name,
+ function_name,
+ function_author,
+ gen_type,
+ gen_path,
+ remark,
+ create_by,
+ create_time
+ )values(
+ #{tableName},
+ #{tableComment},
+ #{className},
+ #{tplCategory},
+ #{tplWebType},
+ #{packageName},
+ #{moduleName},
+ #{businessName},
+ #{functionName},
+ #{functionAuthor},
+ #{genType},
+ #{genPath},
+ #{remark},
+ #{createBy},
+ sysdate() -- 创建时间为当前系统时间
+ )
+
+
+
+
+ update gen_table
+
+ table_name = #{tableName},
+ table_comment = #{tableComment},
+ sub_table_name = #{subTableName},
+ sub_table_fk_name = #{subTableFkName},
+ class_name = #{className},
+ function_author = #{functionAuthor},
+ gen_type = #{genType},
+ gen_path = #{genPath},
+ tpl_category = #{tplCategory},
+ tpl_web_type = #{tplWebType},
+ package_name = #{packageName},
+ module_name = #{moduleName},
+ business_name = #{businessName},
+ function_name = #{functionName},
+ options = #{options},
+ update_by = #{updateBy},
+ remark = #{remark},
+ update_time = sysdate() -- 更新时间为当前系统时间
+
+ where table_id = #{tableId} -- 按主键更新
+
+
+
+
+ delete from gen_table where table_id in
+
+ #{tableId}
+
+
+
+
\ No newline at end of file
diff --git a/huacai-generator/src/main/resources/vm/java/controller.java.vm b/huacai-generator/src/main/resources/vm/java/controller.java.vm
new file mode 100644
index 0000000..6617abd
--- /dev/null
+++ b/huacai-generator/src/main/resources/vm/java/controller.java.vm
@@ -0,0 +1,162 @@
+package ${packageName}.controller;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.huacai.common.annotation.Log;
+import com.huacai.common.core.controller.BaseController;
+import com.huacai.common.core.domain.AjaxResult;
+import com.huacai.common.enums.BusinessType;
+import java.io.InputStream;
+import org.springframework.web.multipart.MultipartFile;
+import ${packageName}.domain.${ClassName};
+import ${packageName}.service.I${ClassName}Service;
+import com.huacai.common.utils.poi.ExcelUtil;
+#if($table.crud || $table.sub)
+import com.huacai.common.core.page.TableDataInfo;
+#elseif($table.tree)
+#end
+
+/**
+ * ${functionName}前端控制器
+ * 提供${functionName}的CRUD、导入导出等接口服务
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+@RestController
+@RequestMapping("/${moduleName}/${businessName}")
+public class ${ClassName}Controller extends BaseController
+{
+ @Autowired
+ private I${ClassName}Service ${className}Service;
+
+/**
+ * 查询${functionName}列表
+ * 支持分页查询和条件过滤
+ */
+@PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") // 权限控制:需要${permissionPrefix}:list权限
+@GetMapping("/list")
+ #if($table.crud || $table.sub)
+ public TableDataInfo list(${ClassName} ${className})
+ {
+ startPage(); // 开启分页(BaseController提供的分页工具)
+ // 调用Service层查询列表
+ List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
+ return getDataTable(list); // 包装成分页响应对象
+ }
+ #elseif($table.tree)
+ public AjaxResult list(${ClassName} ${className})
+ {
+ // 树结构数据不需要分页,直接返回列表
+ List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
+ return success(list); // 包装成成功响应
+ }
+ #end
+
+ /**
+ * 导出${functionName}列表
+ * 将查询结果导出为Excel文件
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')") // 权限控制:需要${permissionPrefix}:export权限
+ @Log(title = "${functionName}", businessType = BusinessType.EXPORT) // 操作日志记录
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, ${ClassName} ${className})
+ {
+ // 查询待导出的数据列表
+ List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
+ // 使用Excel工具类导出
+ ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class);
+ util.exportExcel(response, list, "${functionName}数据");
+ }
+
+ /**
+ * 下载导入模板
+ * 提供标准的Excel导入模板供用户填写数据
+ */
+ @PostMapping("/importTemplate")
+ public void importTemplate(HttpServletResponse response)
+ {
+ ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class);
+ util.importTemplateExcel(response, "${functionName}数据");
+ }
+
+ /**
+ * 导入数据
+ * 从Excel文件导入数据并批量保存
+ */
+ @Log(title = "${functionName}", businessType = BusinessType.IMPORT) // 操作日志记录
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:import')") // 权限控制:需要${permissionPrefix}:import权限
+ @PostMapping("/importData")
+ public AjaxResult importData(MultipartFile file) throws Exception
+ {
+ ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class);
+ InputStream inputStream = file.getInputStream();
+ // 解析Excel文件得到数据列表
+ List<${ClassName}> list = util.importExcel(inputStream );
+ inputStream.close();
+ // 调用Service批量插入
+ int count = ${className}Service.batchInsert${ClassName}(list);
+ return AjaxResult.success("导入成功" + count + "条信息!");
+ }
+
+ /**
+ * 获取${functionName}详细信息
+ * 根据主键查询单条记录详情
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')") // 权限控制:需要${permissionPrefix}:query权限
+ @GetMapping(value = "/{${pkColumn.javaField}}")
+ public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField})
+ {
+ // 调用Service查询详情
+ return success(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}));
+ }
+
+ /**
+ * 新增${functionName}
+ * 新增一条记录
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')") // 权限控制:需要${permissionPrefix}:add权限
+ @Log(title = "${functionName}", businessType = BusinessType.INSERT) // 操作日志记录
+ @PostMapping
+ public AjaxResult add(@RequestBody ${ClassName} ${className})
+ {
+ // 调用Service插入数据,返回影响行数
+ return toAjax(${className}Service.insert${ClassName}(${className}));
+ }
+
+ /**
+ * 修改${functionName}
+ * 更新一条记录
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')") // 权限控制:需要${permissionPrefix}:edit权限
+ @Log(title = "${functionName}", businessType = BusinessType.UPDATE) // 操作日志记录
+ @PutMapping
+ public AjaxResult edit(@RequestBody ${ClassName} ${className})
+ {
+ // 调用Service更新数据,返回影响行数
+ return toAjax(${className}Service.update${ClassName}(${className}));
+ }
+
+ /**
+ * 删除${functionName}
+ * 批量删除记录
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')") // 权限控制:需要${permissionPrefix}:remove权限
+ @Log(title = "${functionName}", businessType = BusinessType.DELETE) // 操作日志记录
+ @DeleteMapping("/{${pkColumn.javaField}s}")
+ public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s)
+ {
+ // 调用Service批量删除,返回影响行数
+ return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s));
+ }
+}
\ No newline at end of file
diff --git a/huacai-generator/src/main/resources/vm/java/domain.java.vm b/huacai-generator/src/main/resources/vm/java/domain.java.vm
new file mode 100644
index 0000000..75a016c
--- /dev/null
+++ b/huacai-generator/src/main/resources/vm/java/domain.java.vm
@@ -0,0 +1,59 @@
+package ${packageName}.domain;
+
+ #foreach ($import in $importList)
+ import ${import};
+ #end
+import com.huacai.common.annotation.Excel;
+import lombok.*;
+import com.huacai.common.core.domain.BaseEntity;
+
+/**
+ * ${functionName}对象 ${tableName}
+ * 对应数据库表:${tableName},用于封装${functionName}相关数据
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+ #if($table.crud || $table.sub)
+ #set($Entity="BaseEntity") // 单表/主子表继承基础实体类(含创建时间、更新时间等通用字段)
+ #elseif($table.tree)
+ #set($Entity="TreeEntity") // 树表继承树形实体类(含父ID、排序等树形结构字段)
+ #end
+ @EqualsAndHashCode(callSuper = true) // Lombok注解:生成equals和hashCode方法,包含父类字段
+ @Data // Lombok注解:自动生成getter、setter、toString等方法
+ @AllArgsConstructor // Lombok注解:生成全参构造方法
+ @NoArgsConstructor // Lombok注解:生成无参构造方法
+ public class ${ClassName} extends ${Entity}
+ {
+ private static final long serialVersionUID = 1L; // 序列化版本号
+
+ #foreach ($column in $columns)
+ #if(!$table.isSuperColumn($column.javaField)) // 排除父类已定义的字段(如id、createTime等)
+ /** $column.columnComment */ // 字段注释,取自数据库字段说明
+ #if($column.list) // 仅对列表显示字段添加Excel注解(用于导出)
+ #set($parentheseIndex=$column.columnComment.indexOf("(")) // 处理注释中的括号内容(如状态(0正常1停用))
+ #if($parentheseIndex != -1)
+ #set($comment=$column.columnComment.substring(0, $parentheseIndex)) // 截取括号前的纯注释
+ #else
+ #set($comment=$column.columnComment)
+ #end
+ #if($parentheseIndex != -1)
+ @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") // 带字典转换的Excel注解
+ #elseif($column.javaType == 'Date')
+ @JsonFormat(pattern = "yyyy-MM-dd") // 日期格式化注解
+ @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") // 日期类型Excel注解
+ #else
+ @Excel(name = "${comment}") // 普通字段Excel注解
+ #end
+ #end
+ private $column.javaType $column.javaField; // 字段定义(类型+名称)
+
+ #end
+ #end
+ #if($table.sub) // 主子表场景:添加子表集合字段
+ /** $table.subTable.functionName信息 */
+ private List<${subClassName}> ${subclassName}List; // 子表数据列表(一对多关系)
+
+ #end
+
+ }
diff --git a/huacai-generator/src/main/resources/vm/java/mapper.java.vm b/huacai-generator/src/main/resources/vm/java/mapper.java.vm
new file mode 100644
index 0000000..7e7d7c2
--- /dev/null
+++ b/huacai-generator/src/main/resources/vm/java/mapper.java.vm
@@ -0,0 +1,91 @@
+package ${packageName}.mapper;
+
+import java.util.List;
+import ${packageName}.domain.${ClassName};
+#if($table.sub)
+import ${packageName}.domain.${subClassName};
+#end
+
+/**
+ * ${functionName}Mapper接口
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+public interface ${ClassName}Mapper
+{
+ /**
+ * 查询${functionName}
+ *
+ * @param ${pkColumn.javaField} ${functionName}主键
+ * @return ${functionName}
+ */
+ public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField});
+
+ /**
+ * 查询${functionName}列表
+ *
+ * @param ${className} ${functionName}
+ * @return ${functionName}集合
+ */
+ public List<${ClassName}> select${ClassName}List(${ClassName} ${className});
+
+ /**
+ * 新增${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+ public int insert${ClassName}(${ClassName} ${className});
+
+ /**
+ * 修改${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+ public int update${ClassName}(${ClassName} ${className});
+
+ /**
+ * 删除${functionName}
+ *
+ * @param ${pkColumn.javaField} ${functionName}主键
+ * @return 结果
+ */
+ public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField});
+
+ /**
+ * 批量删除${functionName}
+ *
+ * @param ${pkColumn.javaField}s 需要删除的数据主键集合
+ * @return 结果
+ */
+ public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s);
+#if($table.sub)
+
+ /**
+ * 批量删除${subTable.functionName}
+ *
+ * @param ${pkColumn.javaField}s 需要删除的数据主键集合
+ * @return 结果
+ */
+ public int delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaType}[] ${pkColumn.javaField}s);
+
+ /**
+ * 批量新增${subTable.functionName}
+ *
+ * @param ${subclassName}List ${subTable.functionName}列表
+ * @return 结果
+ */
+ public int batch${subClassName}(List<${subClassName}> ${subclassName}List);
+
+
+ /**
+ * 通过${functionName}主键删除${subTable.functionName}信息
+ *
+ * @param ${pkColumn.javaField} ${functionName}ID
+ * @return 结果
+ */
+ public int delete${subClassName}By${subTableFkClassName}(${pkColumn.javaType} ${pkColumn.javaField});
+#end
+}
diff --git a/huacai-generator/src/main/resources/vm/java/service.java.vm b/huacai-generator/src/main/resources/vm/java/service.java.vm
new file mode 100644
index 0000000..2dffb14
--- /dev/null
+++ b/huacai-generator/src/main/resources/vm/java/service.java.vm
@@ -0,0 +1,69 @@
+package ${packageName}.service;
+
+import java.util.List;
+import ${packageName}.domain.${ClassName};
+
+/**
+ * ${functionName}Service接口
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+public interface I${ClassName}Service
+{
+ /**
+ * 查询${functionName}
+ *
+ * @param ${pkColumn.javaField} ${functionName}主键
+ * @return ${functionName}
+ */
+ public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField});
+
+ /**
+ * 查询${functionName}列表
+ *
+ * @param ${className} ${functionName}
+ * @return ${functionName}集合
+ */
+ public List<${ClassName}> select${ClassName}List(${ClassName} ${className});
+
+ /**
+ * 新增${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+ public int insert${ClassName}(${ClassName} ${className});
+
+ /**
+ * 批量新增${functionName}
+ *
+ * @param ${className}s ${functionName}List
+ * @return 结果
+ */
+ public int batchInsert${ClassName}(List<${ClassName}> ${className}s);
+
+ /**
+ * 修改${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+ public int update${ClassName}(${ClassName} ${className});
+
+ /**
+ * 批量删除${functionName}
+ *
+ * @param ${pkColumn.javaField}s 需要删除的${functionName}主键集合
+ * @return 结果
+ */
+ public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s);
+
+ /**
+ * 删除${functionName}信息
+ *
+ * @param ${pkColumn.javaField} ${functionName}主键
+ * @return 结果
+ */
+ public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField});
+}
diff --git a/huacai-generator/src/main/resources/vm/java/serviceImpl.java.vm b/huacai-generator/src/main/resources/vm/java/serviceImpl.java.vm
new file mode 100644
index 0000000..b6d2e64
--- /dev/null
+++ b/huacai-generator/src/main/resources/vm/java/serviceImpl.java.vm
@@ -0,0 +1,211 @@
+package ${packageName}.service.impl;
+
+import java.util.List;
+#foreach ($column in $columns)
+#if($column.javaField == 'createTime' || $column.javaField == 'updateTime')
+import com.huacai.common.utils.DateUtils;
+#break
+#end
+#end
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+#if($table.sub)
+import java.util.ArrayList;
+import com.huacai.common.utils.StringUtils;
+import org.springframework.transaction.annotation.Transactional;
+import ${packageName}.domain.${subClassName};
+#end
+import ${packageName}.mapper.${ClassName}Mapper;
+import ${packageName}.domain.${ClassName};
+import ${packageName}.service.I${ClassName}Service;
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * ${functionName}Service业务层处理
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+@Service
+public class ${ClassName}ServiceImpl implements I${ClassName}Service
+{
+ @Autowired
+ private ${ClassName}Mapper ${className}Mapper;
+
+ @Autowired
+ private SqlSessionFactory sqlSessionFactory;
+
+ /**
+ * 查询${functionName}
+ *
+ * @param ${pkColumn.javaField} ${functionName}主键
+ * @return ${functionName}
+ */
+ @Override
+ public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField})
+ {
+ return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField});
+ }
+
+ /**
+ * 查询${functionName}列表
+ *
+ * @param ${className} ${functionName}
+ * @return ${functionName}
+ */
+ @Override
+ public List<${ClassName}> select${ClassName}List(${ClassName} ${className})
+ {
+ return ${className}Mapper.select${ClassName}List(${className});
+ }
+
+ /**
+ * 新增${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+#if($table.sub)
+ @Transactional
+#end
+ @Override
+ public int insert${ClassName}(${ClassName} ${className})
+ {
+#foreach ($column in $columns)
+#if($column.javaField == 'createTime')
+ ${className}.setCreateTime(DateUtils.getNowDate());
+#end
+#end
+#if($table.sub)
+ int rows = ${className}Mapper.insert${ClassName}(${className});
+ insert${subClassName}(${className});
+ return rows;
+#else
+ return ${className}Mapper.insert${ClassName}(${className});
+#end
+ }
+
+ /**
+ * 批量新增${functionName}
+ *
+ * @param ${className}s ${functionName}List
+ * @return 结果
+ */
+ @Override
+ public int batchInsert${ClassName}(List<${ClassName}> ${className}s)
+ {
+ SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
+ int count = 0;
+ if (!CollectionUtils.isEmpty(${className}s)) {
+ try {
+ for (int i = 0; i < ${className}s.size(); i++) {
+ int row = ${className}Mapper.insert${ClassName}(${className}s.get(i));
+ // 防止内存溢出,每100次提交一次,并清除缓存
+ boolean bool = (i >0 && i%100 == 0) || i == ${className}s.size() - 1;
+ if (bool){
+ sqlSession.commit();
+ sqlSession.clearCache();
+ }
+ count = i + 1;
+ }
+ }catch (Exception e){
+ e.printStackTrace();
+ // 没有提交的数据可以回滚
+ sqlSession.rollback();
+ }finally {
+ sqlSession.close();
+ return count;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * 修改${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+#if($table.sub)
+ @Transactional
+#end
+ @Override
+ public int update${ClassName}(${ClassName} ${className})
+ {
+#foreach ($column in $columns)
+#if($column.javaField == 'updateTime')
+ ${className}.setUpdateTime(DateUtils.getNowDate());
+#end
+#end
+#if($table.sub)
+ ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}());
+ insert${subClassName}(${className});
+#end
+ return ${className}Mapper.update${ClassName}(${className});
+ }
+
+ /**
+ * 批量删除${functionName}
+ *
+ * @param ${pkColumn.javaField}s 需要删除的${functionName}主键
+ * @return 结果
+ */
+#if($table.sub)
+ @Transactional
+#end
+ @Override
+ public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s)
+ {
+#if($table.sub)
+ ${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s);
+#end
+ return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s);
+ }
+
+ /**
+ * 删除${functionName}信息
+ *
+ * @param ${pkColumn.javaField} ${functionName}主键
+ * @return 结果
+ */
+#if($table.sub)
+ @Transactional
+#end
+ @Override
+ public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField})
+ {
+#if($table.sub)
+ ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField});
+#end
+ return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField});
+ }
+#if($table.sub)
+
+ /**
+ * 新增${subTable.functionName}信息
+ *
+ * @param ${className} ${functionName}对象
+ */
+ public void insert${subClassName}(${ClassName} ${className})
+ {
+ List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List();
+ ${pkColumn.javaType} ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}();
+ if (StringUtils.isNotNull(${subclassName}List))
+ {
+ List<${subClassName}> list = new ArrayList<${subClassName}>();
+ for (${subClassName} ${subclassName} : ${subclassName}List)
+ {
+ ${subclassName}.set${subTableFkClassName}(${pkColumn.javaField});
+ list.add(${subclassName});
+ }
+ if (list.size() > 0)
+ {
+ ${className}Mapper.batch${subClassName}(list);
+ }
+ }
+ }
+#end
+}
diff --git a/huacai-generator/src/main/resources/vm/java/sub-domain.java.vm b/huacai-generator/src/main/resources/vm/java/sub-domain.java.vm
new file mode 100644
index 0000000..3b804b0
--- /dev/null
+++ b/huacai-generator/src/main/resources/vm/java/sub-domain.java.vm
@@ -0,0 +1,47 @@
+package ${packageName}.domain;
+
+#foreach ($import in $subImportList)
+import ${import};
+#end
+import com.huacai.common.annotation.Excel;
+import lombok.*;
+import com.huacai.common.core.domain.BaseEntity;
+/**
+ * ${subTable.functionName}对象 ${subTableName}
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ${subClassName} extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+#foreach ($column in $subTable.columns)
+#if(!$table.isSuperColumn($column.javaField))
+ /** $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($parentheseIndex != -1)
+ @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
+#elseif($column.javaType == 'Date')
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")
+#else
+ @Excel(name = "${comment}")
+#end
+#end
+ private $column.javaType $column.javaField;
+
+#end
+#end
+
+}
diff --git a/huacai-generator/src/main/resources/vm/js/api.js.vm b/huacai-generator/src/main/resources/vm/js/api.js.vm
new file mode 100644
index 0000000..9295524
--- /dev/null
+++ b/huacai-generator/src/main/resources/vm/js/api.js.vm
@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+// 查询${functionName}列表
+export function list${BusinessName}(query) {
+ return request({
+ url: '/${moduleName}/${businessName}/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询${functionName}详细
+export function get${BusinessName}(${pkColumn.javaField}) {
+ return request({
+ url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
+ method: 'get'
+ })
+}
+
+// 新增${functionName}
+export function add${BusinessName}(data) {
+ return request({
+ url: '/${moduleName}/${businessName}',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改${functionName}
+export function update${BusinessName}(data) {
+ return request({
+ url: '/${moduleName}/${businessName}',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除${functionName}
+export function del${BusinessName}(${pkColumn.javaField}) {
+ return request({
+ url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
+ method: 'delete'
+ })
+}
diff --git a/huacai-generator/src/main/resources/vm/sql/sql.vm b/huacai-generator/src/main/resources/vm/sql/sql.vm
new file mode 100644
index 0000000..0575583
--- /dev/null
+++ b/huacai-generator/src/main/resources/vm/sql/sql.vm
@@ -0,0 +1,22 @@
+-- 菜单 SQL
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单');
+
+-- 按钮父菜单ID
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, '');
+
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, '');
+
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, '');
+
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, '');
+
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, '');
\ No newline at end of file
diff --git a/huacai-generator/src/main/resources/vm/vue/index-tree.vue.vm b/huacai-generator/src/main/resources/vm/vue/index-tree.vue.vm
new file mode 100644
index 0000000..859f17e
--- /dev/null
+++ b/huacai-generator/src/main/resources/vm/vue/index-tree.vue.vm
@@ -0,0 +1,503 @@
+
+
+
+#foreach($column in $columns)
+#if($column.query)
+#set($dictType=$column.dictType)
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.htmlType == "input")
+
+
+
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
+
+
+
+
+
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
+
+
+
+
+#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+
+
+
+#end
+#end
+#end
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 展开/折叠
+
+
+
+
+
+#foreach($column in $columns)
+#set($javaField=$column.javaField)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.pk)
+#elseif($column.list && $column.htmlType == "datetime")
+
+
+ {{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}
+
+
+#elseif($column.list && $column.htmlType == "imageUpload")
+
+
+
+
+
+#elseif($column.list && "" != $column.dictType)
+
+
+#if($column.htmlType == "checkbox")
+
+#else
+
+#end
+
+
+#elseif($column.list && "" != $javaField)
+#if(${foreach.index} == 1)
+
+#else
+
+#end
+#end
+#end
+
+
+ 修改
+ 新增
+ 删除
+
+
+
+
+
+
+
+#foreach($column in $columns)
+#set($field=$column.javaField)
+#if($column.insert && !$column.pk)
+#if(($column.usableColumn) || (!$column.superColumn))
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#set($dictType=$column.dictType)
+#if("" != $treeParentCode && $column.javaField == $treeParentCode)
+
+
+
+#elseif($column.htmlType == "input")
+
+
+
+#elseif($column.htmlType == "imageUpload")
+
+
+
+#elseif($column.htmlType == "fileUpload")
+
+
+
+#elseif($column.htmlType == "editor")
+
+
+
+#elseif($column.htmlType == "select" && "" != $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "select" && $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "checkbox" && "" != $dictType)
+
+
+
+ {{dict.label}}
+
+
+
+#elseif($column.htmlType == "checkbox" && $dictType)
+
+
+ 请选择字典生成
+
+
+#elseif($column.htmlType == "radio" && "" != $dictType)
+
+
+ {{dict.label}}
+
+
+#elseif($column.htmlType == "radio" && $dictType)
+
+
+ 请选择字典生成
+
+
+#elseif($column.htmlType == "datetime")
+
+
+
+
+#elseif($column.htmlType == "textarea")
+
+
+
+#end
+#end
+#end
+#end
+
+
+
+
+
+
+
diff --git a/huacai-generator/src/main/resources/vm/vue/index.vue.vm b/huacai-generator/src/main/resources/vm/vue/index.vue.vm
new file mode 100644
index 0000000..d2aa807
--- /dev/null
+++ b/huacai-generator/src/main/resources/vm/vue/index.vue.vm
@@ -0,0 +1,712 @@
+
+
+
+
+ #foreach($column in $columns)
+ #if($column.query)
+ #set($dictType=$column.dictType)
+ #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+ #set($parentheseIndex=$column.columnComment.indexOf("("))
+ #if($parentheseIndex != -1)
+ #set($comment=$column.columnComment.substring(0, $parentheseIndex))
+ #else
+ #set($comment=$column.columnComment)
+ #end
+ #if($column.htmlType == "input")
+
+
+
+ #elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
+
+
+
+
+
+ #elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
+
+
+
+
+
+ #elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
+
+
+
+
+ #elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+
+
+
+ #end
+ #end
+ #end
+
+ 搜索
+ 重置
+
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出
+
+
+ 导入
+
+
+
+
+
+
+
+
+ #foreach($column in $columns)
+ #set($javaField=$column.javaField)
+ #set($parentheseIndex=$column.columnComment.indexOf("("))
+ #if($parentheseIndex != -1)
+ #set($comment=$column.columnComment.substring(0, $parentheseIndex))
+ #else
+ #set($comment=$column.columnComment)
+ #end
+ #if($column.pk)
+
+ #elseif($column.list && $column.htmlType == "datetime")
+
+
+ {{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}
+
+
+ #elseif($column.list && $column.htmlType == "imageUpload")
+
+
+
+
+
+ #elseif($column.list && "" != $column.dictType)
+
+
+ #if($column.htmlType == "checkbox")
+
+ #else
+
+ #end
+
+
+ #elseif($column.list && "" != $javaField)
+
+ #end
+ #end
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+ 将文件拖到此处,或点击上传
+
+ 仅允许导入xls、xlsx格式文件。
+ 下载模板
+
+
+
+
+
+
+
+
+ #foreach($column in $columns)
+ #set($field=$column.javaField)
+ #if($column.insert && !$column.pk)
+ #if(($column.usableColumn) || (!$column.superColumn))
+ #set($parentheseIndex=$column.columnComment.indexOf("("))
+ #if($parentheseIndex != -1)
+ #set($comment=$column.columnComment.substring(0, $parentheseIndex))
+ #else
+ #set($comment=$column.columnComment)
+ #end
+ #set($dictType=$column.dictType)
+ #if($column.htmlType == "input")
+
+
+
+ #elseif($column.htmlType == "inputNumber")
+
+
+
+ #elseif($column.htmlType == "imageUpload")
+
+
+
+ #elseif($column.htmlType == "fileUpload")
+
+
+
+ #elseif($column.htmlType == "editor")
+
+
+
+ #elseif($column.htmlType == "select" && "" != $dictType)
+
+
+
+
+
+ #elseif($column.htmlType == "select" && $dictType)
+
+
+
+
+
+ #elseif($column.htmlType == "checkbox" && "" != $dictType)
+
+
+
+ {{dict.label}}
+
+
+
+ #elseif($column.htmlType == "checkbox" && $dictType)
+
+
+ 请选择字典生成
+
+
+ #elseif($column.htmlType == "radio" && "" != $dictType)
+
+
+ {{dict.label}}
+
+
+ #elseif($column.htmlType == "radio" && $dictType)
+
+
+ 请选择字典生成
+
+
+ #elseif($column.htmlType == "datetime")
+
+
+
+
+ #elseif($column.htmlType == "textarea")
+
+
+
+ #end
+ #end
+ #end
+ #end
+ #if($table.sub)
+ ${subTable.functionName}信息
+
+
+ 添加
+
+
+ 删除
+
+
+
+
+
+ #foreach($column in $subTable.columns)
+ #set($javaField=$column.javaField)
+ #set($parentheseIndex=$column.columnComment.indexOf("("))
+ #if($parentheseIndex != -1)
+ #set($comment=$column.columnComment.substring(0, $parentheseIndex))
+ #else
+ #set($comment=$column.columnComment)
+ #end
+ #if($column.pk || $javaField == ${subTableFkclassName})
+ #elseif($column.list && $column.htmlType == "input")
+
+
+
+
+
+ #elseif($column.list && $column.htmlType == "inputNumber")
+
+
+
+
+
+ #elseif($column.list && $column.htmlType == "datetime")
+
+
+
+
+
+ #elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" != $column.dictType)
+
+
+
+
+
+
+
+ #elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" == $column.dictType)
+
+
+
+
+
+
+
+ #end
+ #end
+
+ #end
+
+
+
+
+
+
+
diff --git a/huacai-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm b/huacai-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm
new file mode 100644
index 0000000..c54d62b
--- /dev/null
+++ b/huacai-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm
@@ -0,0 +1,474 @@
+
+
+
+#foreach($column in $columns)
+#if($column.query)
+#set($dictType=$column.dictType)
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.htmlType == "input")
+
+
+
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
+
+
+
+
+
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
+
+
+
+
+#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+
+
+
+#end
+#end
+#end
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 展开/折叠
+
+
+
+
+
+#foreach($column in $columns)
+#set($javaField=$column.javaField)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.pk)
+#elseif($column.list && $column.htmlType == "datetime")
+
+
+ {{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}
+
+
+#elseif($column.list && $column.htmlType == "imageUpload")
+
+
+
+
+
+#elseif($column.list && "" != $column.dictType)
+
+
+#if($column.htmlType == "checkbox")
+
+#else
+
+#end
+
+
+#elseif($column.list && "" != $javaField)
+#if(${foreach.index} == 1)
+
+#else
+
+#end
+#end
+#end
+
+
+ 修改
+ 新增
+ 删除
+
+
+
+
+
+
+
+#foreach($column in $columns)
+#set($field=$column.javaField)
+#if($column.insert && !$column.pk)
+#if(($column.usableColumn) || (!$column.superColumn))
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#set($dictType=$column.dictType)
+#if("" != $treeParentCode && $column.javaField == $treeParentCode)
+
+
+
+#elseif($column.htmlType == "input")
+
+
+
+#elseif($column.htmlType == "imageUpload")
+
+
+
+#elseif($column.htmlType == "fileUpload")
+
+
+
+#elseif($column.htmlType == "editor")
+
+
+
+#elseif($column.htmlType == "select" && "" != $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "select" && $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "checkbox" && "" != $dictType)
+
+
+
+ {{dict.label}}
+
+
+
+#elseif($column.htmlType == "checkbox" && $dictType)
+
+
+ 请选择字典生成
+
+
+#elseif($column.htmlType == "radio" && "" != $dictType)
+
+
+ {{dict.label}}
+
+
+#elseif($column.htmlType == "radio" && $dictType)
+
+
+ 请选择字典生成
+
+
+#elseif($column.htmlType == "datetime")
+
+
+
+
+#elseif($column.htmlType == "textarea")
+
+
+
+#end
+#end
+#end
+#end
+
+
+
+
+
+
+
+
+
diff --git a/huacai-generator/src/main/resources/vm/vue/v3/index.vue.vm b/huacai-generator/src/main/resources/vm/vue/v3/index.vue.vm
new file mode 100644
index 0000000..8b25665
--- /dev/null
+++ b/huacai-generator/src/main/resources/vm/vue/v3/index.vue.vm
@@ -0,0 +1,590 @@
+
+
+
+#foreach($column in $columns)
+#if($column.query)
+#set($dictType=$column.dictType)
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.htmlType == "input")
+
+
+
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
+
+
+
+
+
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
+
+
+
+
+#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+
+
+
+#end
+#end
+#end
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出
+
+
+
+
+
+
+#foreach($column in $columns)
+#set($javaField=$column.javaField)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.pk)
+
+#elseif($column.list && $column.htmlType == "datetime")
+
+
+ {{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}
+
+
+#elseif($column.list && $column.htmlType == "imageUpload")
+
+
+
+
+
+#elseif($column.list && "" != $column.dictType)
+
+
+#if($column.htmlType == "checkbox")
+
+#else
+
+#end
+
+
+#elseif($column.list && "" != $javaField)
+
+#end
+#end
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+#foreach($column in $columns)
+#set($field=$column.javaField)
+#if($column.insert && !$column.pk)
+#if(($column.usableColumn) || (!$column.superColumn))
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#set($dictType=$column.dictType)
+#if($column.htmlType == "input")
+
+
+
+#elseif($column.htmlType == "imageUpload")
+
+
+
+#elseif($column.htmlType == "fileUpload")
+
+
+
+#elseif($column.htmlType == "editor")
+
+
+
+#elseif($column.htmlType == "select" && "" != $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "select" && $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "checkbox" && "" != $dictType)
+
+
+
+ {{dict.label}}
+
+
+
+#elseif($column.htmlType == "checkbox" && $dictType)
+
+
+ 请选择字典生成
+
+
+#elseif($column.htmlType == "radio" && "" != $dictType)
+
+
+ {{dict.label}}
+
+
+#elseif($column.htmlType == "radio" && $dictType)
+
+
+ 请选择字典生成
+
+
+#elseif($column.htmlType == "datetime")
+
+
+
+
+#elseif($column.htmlType == "textarea")
+
+
+
+#end
+#end
+#end
+#end
+#if($table.sub)
+ ${subTable.functionName}信息
+
+
+ 添加
+
+
+ 删除
+
+
+
+
+
+#foreach($column in $subTable.columns)
+#set($javaField=$column.javaField)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.pk || $javaField == ${subTableFkclassName})
+#elseif($column.list && $column.htmlType == "input")
+
+
+
+
+
+#elseif($column.list && $column.htmlType == "datetime")
+
+
+
+
+
+
+#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" != $column.dictType)
+
+
+
+
+
+
+
+#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" == $column.dictType)
+
+
+
+
+
+
+
+#end
+#end
+
+#end
+
+
+
+
+
+
+
+
+
diff --git a/huacai-generator/src/main/resources/vm/xml/mapper.xml.vm b/huacai-generator/src/main/resources/vm/xml/mapper.xml.vm
new file mode 100644
index 0000000..4233b10
--- /dev/null
+++ b/huacai-generator/src/main/resources/vm/xml/mapper.xml.vm
@@ -0,0 +1,135 @@
+
+
+
+
+
+#foreach ($column in $columns)
+
+#end
+
+#if($table.sub)
+
+
+
+
+
+
+#foreach ($column in $subTable.columns)
+
+#end
+
+#end
+
+
+ select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end from ${tableName}
+
+
+
+
+
+
+
+ insert into ${tableName}
+
+#foreach($column in $columns)
+#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment)
+ $column.columnName,
+#end
+#end
+
+
+#foreach($column in $columns)
+#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment)
+ #{$column.javaField},
+#end
+#end
+
+
+
+
+ update ${tableName}
+
+#foreach($column in $columns)
+#if($column.columnName != $pkColumn.columnName)
+ $column.columnName = #{$column.javaField},
+#end
+#end
+
+ where ${pkColumn.columnName} = #{${pkColumn.javaField}}
+
+
+
+ delete from ${tableName} where ${pkColumn.columnName} = #{${pkColumn.javaField}}
+
+
+
+ delete from ${tableName} where ${pkColumn.columnName} in
+
+ #{${pkColumn.javaField}}
+
+
+#if($table.sub)
+
+
+ delete from ${subTableName} where ${subTableFkName} in
+
+ #{${subTableFkclassName}}
+
+
+
+
+ delete from ${subTableName} where ${subTableFkName} = #{${subTableFkclassName}}
+
+
+
+ insert into ${subTableName}(#foreach($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end) values
+
+ (#foreach($column in $subTable.columns) #{item.$column.javaField}#if($foreach.count != $subTable.columns.size()),#end#end)
+
+
+#end
+
diff --git a/huacai-generator/target/classes/com/huacai/generator/config/GenConfig.class b/huacai-generator/target/classes/com/huacai/generator/config/GenConfig.class
new file mode 100644
index 0000000..3f57dd0
Binary files /dev/null and b/huacai-generator/target/classes/com/huacai/generator/config/GenConfig.class differ
diff --git a/huacai-generator/target/classes/com/huacai/generator/controller/GenController.class b/huacai-generator/target/classes/com/huacai/generator/controller/GenController.class
new file mode 100644
index 0000000..e5b34af
Binary files /dev/null and b/huacai-generator/target/classes/com/huacai/generator/controller/GenController.class differ
diff --git a/huacai-generator/target/classes/com/huacai/generator/domain/GenTable.class b/huacai-generator/target/classes/com/huacai/generator/domain/GenTable.class
new file mode 100644
index 0000000..eb4e12f
Binary files /dev/null and b/huacai-generator/target/classes/com/huacai/generator/domain/GenTable.class differ
diff --git a/huacai-generator/target/classes/com/huacai/generator/domain/GenTableColumn.class b/huacai-generator/target/classes/com/huacai/generator/domain/GenTableColumn.class
new file mode 100644
index 0000000..44b3523
Binary files /dev/null and b/huacai-generator/target/classes/com/huacai/generator/domain/GenTableColumn.class differ
diff --git a/huacai-generator/target/classes/com/huacai/generator/mapper/GenTableColumnMapper.class b/huacai-generator/target/classes/com/huacai/generator/mapper/GenTableColumnMapper.class
new file mode 100644
index 0000000..2468bed
Binary files /dev/null and b/huacai-generator/target/classes/com/huacai/generator/mapper/GenTableColumnMapper.class differ
diff --git a/huacai-generator/target/classes/com/huacai/generator/mapper/GenTableMapper.class b/huacai-generator/target/classes/com/huacai/generator/mapper/GenTableMapper.class
new file mode 100644
index 0000000..b3b41dc
Binary files /dev/null and b/huacai-generator/target/classes/com/huacai/generator/mapper/GenTableMapper.class differ
diff --git a/huacai-generator/target/classes/com/huacai/generator/service/GenTableColumnServiceImpl.class b/huacai-generator/target/classes/com/huacai/generator/service/GenTableColumnServiceImpl.class
new file mode 100644
index 0000000..bb5c2b2
Binary files /dev/null and b/huacai-generator/target/classes/com/huacai/generator/service/GenTableColumnServiceImpl.class differ
diff --git a/huacai-generator/target/classes/com/huacai/generator/service/GenTableServiceImpl.class b/huacai-generator/target/classes/com/huacai/generator/service/GenTableServiceImpl.class
new file mode 100644
index 0000000..9575146
Binary files /dev/null and b/huacai-generator/target/classes/com/huacai/generator/service/GenTableServiceImpl.class differ
diff --git a/huacai-generator/target/classes/com/huacai/generator/service/IGenTableColumnService.class b/huacai-generator/target/classes/com/huacai/generator/service/IGenTableColumnService.class
new file mode 100644
index 0000000..b47bfa3
Binary files /dev/null and b/huacai-generator/target/classes/com/huacai/generator/service/IGenTableColumnService.class differ
diff --git a/huacai-generator/target/classes/com/huacai/generator/service/IGenTableService.class b/huacai-generator/target/classes/com/huacai/generator/service/IGenTableService.class
new file mode 100644
index 0000000..092aead
Binary files /dev/null and b/huacai-generator/target/classes/com/huacai/generator/service/IGenTableService.class differ
diff --git a/huacai-generator/target/classes/com/huacai/generator/util/GenUtils.class b/huacai-generator/target/classes/com/huacai/generator/util/GenUtils.class
new file mode 100644
index 0000000..cf0ef73
Binary files /dev/null and b/huacai-generator/target/classes/com/huacai/generator/util/GenUtils.class differ
diff --git a/huacai-generator/target/classes/com/huacai/generator/util/VelocityInitializer.class b/huacai-generator/target/classes/com/huacai/generator/util/VelocityInitializer.class
new file mode 100644
index 0000000..7637f4d
Binary files /dev/null and b/huacai-generator/target/classes/com/huacai/generator/util/VelocityInitializer.class differ
diff --git a/huacai-generator/target/classes/com/huacai/generator/util/VelocityUtils.class b/huacai-generator/target/classes/com/huacai/generator/util/VelocityUtils.class
new file mode 100644
index 0000000..32d1eff
Binary files /dev/null and b/huacai-generator/target/classes/com/huacai/generator/util/VelocityUtils.class differ
diff --git a/huacai-generator/target/classes/generator.yml b/huacai-generator/target/classes/generator.yml
new file mode 100644
index 0000000..5eb98d3
--- /dev/null
+++ b/huacai-generator/target/classes/generator.yml
@@ -0,0 +1,10 @@
+# 代码生成
+gen:
+ # 作者
+ author: huacai
+ # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
+ packageName: com.huacai
+ # 自动去除表前缀,默认是false
+ autoRemovePre: false
+ # 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
+ tablePrefix: ylxt_
diff --git a/huacai-generator/target/classes/mapper/generator/GenTableColumnMapper.xml b/huacai-generator/target/classes/mapper/generator/GenTableColumnMapper.xml
new file mode 100644
index 0000000..3142541
--- /dev/null
+++ b/huacai-generator/target/classes/mapper/generator/GenTableColumnMapper.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ select column_id, table_id, column_name, column_comment, column_type, java_type, java_field, is_pk, is_increment, is_required, is_insert, is_edit, is_list, is_query, query_type, html_type, dict_type, sort, create_by, create_time, update_by, update_time from gen_table_column
+
+
+
+
+
+
+
+ insert into gen_table_column (
+ table_id,
+ column_name,
+ column_comment,
+ column_type,
+ java_type,
+ java_field,
+ is_pk,
+ is_increment,
+ is_required,
+ is_insert,
+ is_edit,
+ is_list,
+ is_query,
+ query_type,
+ html_type,
+ dict_type,
+ sort,
+ create_by,
+ create_time
+ )values(
+ #{tableId},
+ #{columnName},
+ #{columnComment},
+ #{columnType},
+ #{javaType},
+ #{javaField},
+ #{isPk},
+ #{isIncrement},
+ #{isRequired},
+ #{isInsert},
+ #{isEdit},
+ #{isList},
+ #{isQuery},
+ #{queryType},
+ #{htmlType},
+ #{dictType},
+ #{sort},
+ #{createBy},
+ sysdate()
+ )
+
+
+
+ update gen_table_column
+
+ column_comment = #{columnComment},
+ java_type = #{javaType},
+ java_field = #{javaField},
+ is_insert = #{isInsert},
+ is_edit = #{isEdit},
+ is_list = #{isList},
+ is_query = #{isQuery},
+ is_required = #{isRequired},
+ query_type = #{queryType},
+ html_type = #{htmlType},
+ dict_type = #{dictType},
+ sort = #{sort},
+ update_by = #{updateBy},
+ update_time = sysdate()
+
+ where column_id = #{columnId}
+
+
+
+ delete from gen_table_column where table_id in
+
+ #{tableId}
+
+
+
+
+ delete from gen_table_column where column_id in
+
+ #{item.columnId}
+
+
+
+
diff --git a/huacai-generator/target/classes/mapper/generator/GenTableMapper.xml b/huacai-generator/target/classes/mapper/generator/GenTableMapper.xml
new file mode 100644
index 0000000..7f17e71
--- /dev/null
+++ b/huacai-generator/target/classes/mapper/generator/GenTableMapper.xml
@@ -0,0 +1,206 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, tpl_web_type, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ insert into gen_table (
+ table_name,
+ table_comment,
+ class_name,
+ tpl_category,
+ tpl_web_type,
+ package_name,
+ module_name,
+ business_name,
+ function_name,
+ function_author,
+ gen_type,
+ gen_path,
+ remark,
+ create_by,
+ create_time
+ )values(
+ #{tableName},
+ #{tableComment},
+ #{className},
+ #{tplCategory},
+ #{tplWebType},
+ #{packageName},
+ #{moduleName},
+ #{businessName},
+ #{functionName},
+ #{functionAuthor},
+ #{genType},
+ #{genPath},
+ #{remark},
+ #{createBy},
+ sysdate()
+ )
+
+
+
+ update gen_table
+
+ table_name = #{tableName},
+ table_comment = #{tableComment},
+ sub_table_name = #{subTableName},
+ sub_table_fk_name = #{subTableFkName},
+ class_name = #{className},
+ function_author = #{functionAuthor},
+ gen_type = #{genType},
+ gen_path = #{genPath},
+ tpl_category = #{tplCategory},
+ tpl_web_type = #{tplWebType},
+ package_name = #{packageName},
+ module_name = #{moduleName},
+ business_name = #{businessName},
+ function_name = #{functionName},
+ options = #{options},
+ update_by = #{updateBy},
+ remark = #{remark},
+ update_time = sysdate()
+
+ where table_id = #{tableId}
+
+
+
+ delete from gen_table where table_id in
+
+ #{tableId}
+
+
+
+
diff --git a/huacai-generator/target/classes/vm/java/controller.java.vm b/huacai-generator/target/classes/vm/java/controller.java.vm
new file mode 100644
index 0000000..971bc58
--- /dev/null
+++ b/huacai-generator/target/classes/vm/java/controller.java.vm
@@ -0,0 +1,143 @@
+package ${packageName}.controller;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.huacai.common.annotation.Log;
+import com.huacai.common.core.controller.BaseController;
+import com.huacai.common.core.domain.AjaxResult;
+import com.huacai.common.enums.BusinessType;
+import java.io.InputStream;
+import org.springframework.web.multipart.MultipartFile;
+import ${packageName}.domain.${ClassName};
+import ${packageName}.service.I${ClassName}Service;
+import com.huacai.common.utils.poi.ExcelUtil;
+#if($table.crud || $table.sub)
+import com.huacai.common.core.page.TableDataInfo;
+#elseif($table.tree)
+#end
+
+/**
+ * ${functionName}Controller
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+@RestController
+@RequestMapping("/${moduleName}/${businessName}")
+public class ${ClassName}Controller extends BaseController
+{
+ @Autowired
+ private I${ClassName}Service ${className}Service;
+
+ /**
+ * 查询${functionName}列表
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')")
+ @GetMapping("/list")
+#if($table.crud || $table.sub)
+ public TableDataInfo list(${ClassName} ${className})
+ {
+ startPage();
+ List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
+ return getDataTable(list);
+ }
+#elseif($table.tree)
+ public AjaxResult list(${ClassName} ${className})
+ {
+ List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
+ return success(list);
+ }
+#end
+
+ /**
+ * 导出${functionName}列表
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')")
+ @Log(title = "${functionName}", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, ${ClassName} ${className})
+ {
+ List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
+ ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class);
+ util.exportExcel(response, list, "${functionName}数据");
+ }
+
+ /**
+ * 下载模板
+ */
+ @PostMapping("/importTemplate")
+ public void importTemplate(HttpServletResponse response)
+ {
+ ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class);
+ util.importTemplateExcel(response, "${functionName}数据");
+ }
+
+ /**
+ * 导入数据
+ */
+ @Log(title = "${functionName}", businessType = BusinessType.IMPORT)
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:import')")
+ @PostMapping("/importData")
+ public AjaxResult importData(MultipartFile file) throws Exception
+ {
+ ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class);
+ InputStream inputStream = file.getInputStream();
+ List<${ClassName}> list = util.importExcel(inputStream );
+ inputStream.close();
+ int count = ${className}Service.batchInsert${ClassName}(list);
+ return AjaxResult.success("导入成功" + count + "条信息!");
+ }
+
+ /**
+ * 获取${functionName}详细信息
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')")
+ @GetMapping(value = "/{${pkColumn.javaField}}")
+ public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField})
+ {
+ return success(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}));
+ }
+
+ /**
+ * 新增${functionName}
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')")
+ @Log(title = "${functionName}", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@RequestBody ${ClassName} ${className})
+ {
+ return toAjax(${className}Service.insert${ClassName}(${className}));
+ }
+
+ /**
+ * 修改${functionName}
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')")
+ @Log(title = "${functionName}", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@RequestBody ${ClassName} ${className})
+ {
+ return toAjax(${className}Service.update${ClassName}(${className}));
+ }
+
+ /**
+ * 删除${functionName}
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')")
+ @Log(title = "${functionName}", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{${pkColumn.javaField}s}")
+ public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s)
+ {
+ return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s));
+ }
+}
diff --git a/huacai-generator/target/classes/vm/java/domain.java.vm b/huacai-generator/target/classes/vm/java/domain.java.vm
new file mode 100644
index 0000000..c608b41
--- /dev/null
+++ b/huacai-generator/target/classes/vm/java/domain.java.vm
@@ -0,0 +1,61 @@
+package ${packageName}.domain;
+
+#foreach ($import in $importList)
+import ${import};
+#end
+import com.huacai.common.annotation.Excel;
+#if($table.crud || $table.sub)
+#elseif($table.tree)
+#end
+import lombok.*;
+import com.huacai.common.core.domain.BaseEntity;
+
+/**
+ * ${functionName}对象 ${tableName}
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+#if($table.crud || $table.sub)
+#set($Entity="BaseEntity")
+#elseif($table.tree)
+#set($Entity="TreeEntity")
+#end
+@EqualsAndHashCode(callSuper = true)
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ${ClassName} extends ${Entity}
+{
+ private static final long serialVersionUID = 1L;
+
+#foreach ($column in $columns)
+#if(!$table.isSuperColumn($column.javaField))
+ /** $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($parentheseIndex != -1)
+ @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
+#elseif($column.javaType == 'Date')
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")
+#else
+ @Excel(name = "${comment}")
+#end
+#end
+ private $column.javaType $column.javaField;
+
+#end
+#end
+#if($table.sub)
+ /** $table.subTable.functionName信息 */
+ private List<${subClassName}> ${subclassName}List;
+
+#end
+
+}
diff --git a/huacai-generator/target/classes/vm/java/mapper.java.vm b/huacai-generator/target/classes/vm/java/mapper.java.vm
new file mode 100644
index 0000000..7e7d7c2
--- /dev/null
+++ b/huacai-generator/target/classes/vm/java/mapper.java.vm
@@ -0,0 +1,91 @@
+package ${packageName}.mapper;
+
+import java.util.List;
+import ${packageName}.domain.${ClassName};
+#if($table.sub)
+import ${packageName}.domain.${subClassName};
+#end
+
+/**
+ * ${functionName}Mapper接口
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+public interface ${ClassName}Mapper
+{
+ /**
+ * 查询${functionName}
+ *
+ * @param ${pkColumn.javaField} ${functionName}主键
+ * @return ${functionName}
+ */
+ public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField});
+
+ /**
+ * 查询${functionName}列表
+ *
+ * @param ${className} ${functionName}
+ * @return ${functionName}集合
+ */
+ public List<${ClassName}> select${ClassName}List(${ClassName} ${className});
+
+ /**
+ * 新增${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+ public int insert${ClassName}(${ClassName} ${className});
+
+ /**
+ * 修改${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+ public int update${ClassName}(${ClassName} ${className});
+
+ /**
+ * 删除${functionName}
+ *
+ * @param ${pkColumn.javaField} ${functionName}主键
+ * @return 结果
+ */
+ public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField});
+
+ /**
+ * 批量删除${functionName}
+ *
+ * @param ${pkColumn.javaField}s 需要删除的数据主键集合
+ * @return 结果
+ */
+ public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s);
+#if($table.sub)
+
+ /**
+ * 批量删除${subTable.functionName}
+ *
+ * @param ${pkColumn.javaField}s 需要删除的数据主键集合
+ * @return 结果
+ */
+ public int delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaType}[] ${pkColumn.javaField}s);
+
+ /**
+ * 批量新增${subTable.functionName}
+ *
+ * @param ${subclassName}List ${subTable.functionName}列表
+ * @return 结果
+ */
+ public int batch${subClassName}(List<${subClassName}> ${subclassName}List);
+
+
+ /**
+ * 通过${functionName}主键删除${subTable.functionName}信息
+ *
+ * @param ${pkColumn.javaField} ${functionName}ID
+ * @return 结果
+ */
+ public int delete${subClassName}By${subTableFkClassName}(${pkColumn.javaType} ${pkColumn.javaField});
+#end
+}
diff --git a/huacai-generator/target/classes/vm/java/service.java.vm b/huacai-generator/target/classes/vm/java/service.java.vm
new file mode 100644
index 0000000..2dffb14
--- /dev/null
+++ b/huacai-generator/target/classes/vm/java/service.java.vm
@@ -0,0 +1,69 @@
+package ${packageName}.service;
+
+import java.util.List;
+import ${packageName}.domain.${ClassName};
+
+/**
+ * ${functionName}Service接口
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+public interface I${ClassName}Service
+{
+ /**
+ * 查询${functionName}
+ *
+ * @param ${pkColumn.javaField} ${functionName}主键
+ * @return ${functionName}
+ */
+ public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField});
+
+ /**
+ * 查询${functionName}列表
+ *
+ * @param ${className} ${functionName}
+ * @return ${functionName}集合
+ */
+ public List<${ClassName}> select${ClassName}List(${ClassName} ${className});
+
+ /**
+ * 新增${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+ public int insert${ClassName}(${ClassName} ${className});
+
+ /**
+ * 批量新增${functionName}
+ *
+ * @param ${className}s ${functionName}List
+ * @return 结果
+ */
+ public int batchInsert${ClassName}(List<${ClassName}> ${className}s);
+
+ /**
+ * 修改${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+ public int update${ClassName}(${ClassName} ${className});
+
+ /**
+ * 批量删除${functionName}
+ *
+ * @param ${pkColumn.javaField}s 需要删除的${functionName}主键集合
+ * @return 结果
+ */
+ public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s);
+
+ /**
+ * 删除${functionName}信息
+ *
+ * @param ${pkColumn.javaField} ${functionName}主键
+ * @return 结果
+ */
+ public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField});
+}
diff --git a/huacai-generator/target/classes/vm/java/serviceImpl.java.vm b/huacai-generator/target/classes/vm/java/serviceImpl.java.vm
new file mode 100644
index 0000000..b6d2e64
--- /dev/null
+++ b/huacai-generator/target/classes/vm/java/serviceImpl.java.vm
@@ -0,0 +1,211 @@
+package ${packageName}.service.impl;
+
+import java.util.List;
+#foreach ($column in $columns)
+#if($column.javaField == 'createTime' || $column.javaField == 'updateTime')
+import com.huacai.common.utils.DateUtils;
+#break
+#end
+#end
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+#if($table.sub)
+import java.util.ArrayList;
+import com.huacai.common.utils.StringUtils;
+import org.springframework.transaction.annotation.Transactional;
+import ${packageName}.domain.${subClassName};
+#end
+import ${packageName}.mapper.${ClassName}Mapper;
+import ${packageName}.domain.${ClassName};
+import ${packageName}.service.I${ClassName}Service;
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * ${functionName}Service业务层处理
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+@Service
+public class ${ClassName}ServiceImpl implements I${ClassName}Service
+{
+ @Autowired
+ private ${ClassName}Mapper ${className}Mapper;
+
+ @Autowired
+ private SqlSessionFactory sqlSessionFactory;
+
+ /**
+ * 查询${functionName}
+ *
+ * @param ${pkColumn.javaField} ${functionName}主键
+ * @return ${functionName}
+ */
+ @Override
+ public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField})
+ {
+ return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField});
+ }
+
+ /**
+ * 查询${functionName}列表
+ *
+ * @param ${className} ${functionName}
+ * @return ${functionName}
+ */
+ @Override
+ public List<${ClassName}> select${ClassName}List(${ClassName} ${className})
+ {
+ return ${className}Mapper.select${ClassName}List(${className});
+ }
+
+ /**
+ * 新增${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+#if($table.sub)
+ @Transactional
+#end
+ @Override
+ public int insert${ClassName}(${ClassName} ${className})
+ {
+#foreach ($column in $columns)
+#if($column.javaField == 'createTime')
+ ${className}.setCreateTime(DateUtils.getNowDate());
+#end
+#end
+#if($table.sub)
+ int rows = ${className}Mapper.insert${ClassName}(${className});
+ insert${subClassName}(${className});
+ return rows;
+#else
+ return ${className}Mapper.insert${ClassName}(${className});
+#end
+ }
+
+ /**
+ * 批量新增${functionName}
+ *
+ * @param ${className}s ${functionName}List
+ * @return 结果
+ */
+ @Override
+ public int batchInsert${ClassName}(List<${ClassName}> ${className}s)
+ {
+ SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
+ int count = 0;
+ if (!CollectionUtils.isEmpty(${className}s)) {
+ try {
+ for (int i = 0; i < ${className}s.size(); i++) {
+ int row = ${className}Mapper.insert${ClassName}(${className}s.get(i));
+ // 防止内存溢出,每100次提交一次,并清除缓存
+ boolean bool = (i >0 && i%100 == 0) || i == ${className}s.size() - 1;
+ if (bool){
+ sqlSession.commit();
+ sqlSession.clearCache();
+ }
+ count = i + 1;
+ }
+ }catch (Exception e){
+ e.printStackTrace();
+ // 没有提交的数据可以回滚
+ sqlSession.rollback();
+ }finally {
+ sqlSession.close();
+ return count;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * 修改${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+#if($table.sub)
+ @Transactional
+#end
+ @Override
+ public int update${ClassName}(${ClassName} ${className})
+ {
+#foreach ($column in $columns)
+#if($column.javaField == 'updateTime')
+ ${className}.setUpdateTime(DateUtils.getNowDate());
+#end
+#end
+#if($table.sub)
+ ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}());
+ insert${subClassName}(${className});
+#end
+ return ${className}Mapper.update${ClassName}(${className});
+ }
+
+ /**
+ * 批量删除${functionName}
+ *
+ * @param ${pkColumn.javaField}s 需要删除的${functionName}主键
+ * @return 结果
+ */
+#if($table.sub)
+ @Transactional
+#end
+ @Override
+ public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s)
+ {
+#if($table.sub)
+ ${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s);
+#end
+ return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s);
+ }
+
+ /**
+ * 删除${functionName}信息
+ *
+ * @param ${pkColumn.javaField} ${functionName}主键
+ * @return 结果
+ */
+#if($table.sub)
+ @Transactional
+#end
+ @Override
+ public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField})
+ {
+#if($table.sub)
+ ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField});
+#end
+ return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField});
+ }
+#if($table.sub)
+
+ /**
+ * 新增${subTable.functionName}信息
+ *
+ * @param ${className} ${functionName}对象
+ */
+ public void insert${subClassName}(${ClassName} ${className})
+ {
+ List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List();
+ ${pkColumn.javaType} ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}();
+ if (StringUtils.isNotNull(${subclassName}List))
+ {
+ List<${subClassName}> list = new ArrayList<${subClassName}>();
+ for (${subClassName} ${subclassName} : ${subclassName}List)
+ {
+ ${subclassName}.set${subTableFkClassName}(${pkColumn.javaField});
+ list.add(${subclassName});
+ }
+ if (list.size() > 0)
+ {
+ ${className}Mapper.batch${subClassName}(list);
+ }
+ }
+ }
+#end
+}
diff --git a/huacai-generator/target/classes/vm/java/sub-domain.java.vm b/huacai-generator/target/classes/vm/java/sub-domain.java.vm
new file mode 100644
index 0000000..3b804b0
--- /dev/null
+++ b/huacai-generator/target/classes/vm/java/sub-domain.java.vm
@@ -0,0 +1,47 @@
+package ${packageName}.domain;
+
+#foreach ($import in $subImportList)
+import ${import};
+#end
+import com.huacai.common.annotation.Excel;
+import lombok.*;
+import com.huacai.common.core.domain.BaseEntity;
+/**
+ * ${subTable.functionName}对象 ${subTableName}
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ${subClassName} extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+#foreach ($column in $subTable.columns)
+#if(!$table.isSuperColumn($column.javaField))
+ /** $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($parentheseIndex != -1)
+ @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
+#elseif($column.javaType == 'Date')
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")
+#else
+ @Excel(name = "${comment}")
+#end
+#end
+ private $column.javaType $column.javaField;
+
+#end
+#end
+
+}
diff --git a/huacai-generator/target/classes/vm/js/api.js.vm b/huacai-generator/target/classes/vm/js/api.js.vm
new file mode 100644
index 0000000..9295524
--- /dev/null
+++ b/huacai-generator/target/classes/vm/js/api.js.vm
@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+// 查询${functionName}列表
+export function list${BusinessName}(query) {
+ return request({
+ url: '/${moduleName}/${businessName}/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询${functionName}详细
+export function get${BusinessName}(${pkColumn.javaField}) {
+ return request({
+ url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
+ method: 'get'
+ })
+}
+
+// 新增${functionName}
+export function add${BusinessName}(data) {
+ return request({
+ url: '/${moduleName}/${businessName}',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改${functionName}
+export function update${BusinessName}(data) {
+ return request({
+ url: '/${moduleName}/${businessName}',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除${functionName}
+export function del${BusinessName}(${pkColumn.javaField}) {
+ return request({
+ url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
+ method: 'delete'
+ })
+}
diff --git a/huacai-generator/target/classes/vm/sql/sql.vm b/huacai-generator/target/classes/vm/sql/sql.vm
new file mode 100644
index 0000000..0575583
--- /dev/null
+++ b/huacai-generator/target/classes/vm/sql/sql.vm
@@ -0,0 +1,22 @@
+-- 菜单 SQL
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单');
+
+-- 按钮父菜单ID
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, '');
+
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, '');
+
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, '');
+
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, '');
+
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, '');
\ No newline at end of file
diff --git a/huacai-generator/target/classes/vm/vue/index-tree.vue.vm b/huacai-generator/target/classes/vm/vue/index-tree.vue.vm
new file mode 100644
index 0000000..859f17e
--- /dev/null
+++ b/huacai-generator/target/classes/vm/vue/index-tree.vue.vm
@@ -0,0 +1,503 @@
+
+
+
+#foreach($column in $columns)
+#if($column.query)
+#set($dictType=$column.dictType)
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.htmlType == "input")
+
+
+
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
+
+
+
+
+
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
+
+
+
+
+#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+
+
+
+#end
+#end
+#end
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 展开/折叠
+
+
+
+
+
+#foreach($column in $columns)
+#set($javaField=$column.javaField)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.pk)
+#elseif($column.list && $column.htmlType == "datetime")
+
+
+ {{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}
+
+
+#elseif($column.list && $column.htmlType == "imageUpload")
+
+
+
+
+
+#elseif($column.list && "" != $column.dictType)
+
+
+#if($column.htmlType == "checkbox")
+
+#else
+
+#end
+
+
+#elseif($column.list && "" != $javaField)
+#if(${foreach.index} == 1)
+
+#else
+
+#end
+#end
+#end
+
+
+ 修改
+ 新增
+ 删除
+
+
+
+
+
+
+
+#foreach($column in $columns)
+#set($field=$column.javaField)
+#if($column.insert && !$column.pk)
+#if(($column.usableColumn) || (!$column.superColumn))
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#set($dictType=$column.dictType)
+#if("" != $treeParentCode && $column.javaField == $treeParentCode)
+
+
+
+#elseif($column.htmlType == "input")
+
+
+
+#elseif($column.htmlType == "imageUpload")
+
+
+
+#elseif($column.htmlType == "fileUpload")
+
+
+
+#elseif($column.htmlType == "editor")
+
+
+
+#elseif($column.htmlType == "select" && "" != $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "select" && $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "checkbox" && "" != $dictType)
+
+
+
+ {{dict.label}}
+
+
+
+#elseif($column.htmlType == "checkbox" && $dictType)
+
+
+ 请选择字典生成
+
+
+#elseif($column.htmlType == "radio" && "" != $dictType)
+
+
+ {{dict.label}}
+
+
+#elseif($column.htmlType == "radio" && $dictType)
+
+
+ 请选择字典生成
+
+
+#elseif($column.htmlType == "datetime")
+
+
+
+
+#elseif($column.htmlType == "textarea")
+
+
+
+#end
+#end
+#end
+#end
+
+
+
+
+
+
+
diff --git a/huacai-generator/target/classes/vm/vue/index.vue.vm b/huacai-generator/target/classes/vm/vue/index.vue.vm
new file mode 100644
index 0000000..d2aa807
--- /dev/null
+++ b/huacai-generator/target/classes/vm/vue/index.vue.vm
@@ -0,0 +1,712 @@
+
+
+
+
+ #foreach($column in $columns)
+ #if($column.query)
+ #set($dictType=$column.dictType)
+ #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+ #set($parentheseIndex=$column.columnComment.indexOf("("))
+ #if($parentheseIndex != -1)
+ #set($comment=$column.columnComment.substring(0, $parentheseIndex))
+ #else
+ #set($comment=$column.columnComment)
+ #end
+ #if($column.htmlType == "input")
+
+
+
+ #elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
+
+
+
+
+
+ #elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
+
+
+
+
+
+ #elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
+
+
+
+
+ #elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+
+
+
+ #end
+ #end
+ #end
+
+ 搜索
+ 重置
+
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出
+
+
+ 导入
+
+
+
+
+
+
+
+
+ #foreach($column in $columns)
+ #set($javaField=$column.javaField)
+ #set($parentheseIndex=$column.columnComment.indexOf("("))
+ #if($parentheseIndex != -1)
+ #set($comment=$column.columnComment.substring(0, $parentheseIndex))
+ #else
+ #set($comment=$column.columnComment)
+ #end
+ #if($column.pk)
+
+ #elseif($column.list && $column.htmlType == "datetime")
+
+
+ {{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}
+
+
+ #elseif($column.list && $column.htmlType == "imageUpload")
+
+
+
+
+
+ #elseif($column.list && "" != $column.dictType)
+
+
+ #if($column.htmlType == "checkbox")
+
+ #else
+
+ #end
+
+
+ #elseif($column.list && "" != $javaField)
+
+ #end
+ #end
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+ 将文件拖到此处,或点击上传
+
+ 仅允许导入xls、xlsx格式文件。
+ 下载模板
+
+
+
+
+
+
+
+
+ #foreach($column in $columns)
+ #set($field=$column.javaField)
+ #if($column.insert && !$column.pk)
+ #if(($column.usableColumn) || (!$column.superColumn))
+ #set($parentheseIndex=$column.columnComment.indexOf("("))
+ #if($parentheseIndex != -1)
+ #set($comment=$column.columnComment.substring(0, $parentheseIndex))
+ #else
+ #set($comment=$column.columnComment)
+ #end
+ #set($dictType=$column.dictType)
+ #if($column.htmlType == "input")
+
+
+
+ #elseif($column.htmlType == "inputNumber")
+
+
+
+ #elseif($column.htmlType == "imageUpload")
+
+
+
+ #elseif($column.htmlType == "fileUpload")
+
+
+
+ #elseif($column.htmlType == "editor")
+
+
+
+ #elseif($column.htmlType == "select" && "" != $dictType)
+
+
+
+
+
+ #elseif($column.htmlType == "select" && $dictType)
+
+
+
+
+
+ #elseif($column.htmlType == "checkbox" && "" != $dictType)
+
+
+
+ {{dict.label}}
+
+
+
+ #elseif($column.htmlType == "checkbox" && $dictType)
+
+
+ 请选择字典生成
+
+
+ #elseif($column.htmlType == "radio" && "" != $dictType)
+
+
+ {{dict.label}}
+
+
+ #elseif($column.htmlType == "radio" && $dictType)
+
+
+ 请选择字典生成
+
+
+ #elseif($column.htmlType == "datetime")
+
+
+
+
+ #elseif($column.htmlType == "textarea")
+
+
+
+ #end
+ #end
+ #end
+ #end
+ #if($table.sub)
+ ${subTable.functionName}信息
+
+
+ 添加
+
+
+ 删除
+
+
+
+
+
+ #foreach($column in $subTable.columns)
+ #set($javaField=$column.javaField)
+ #set($parentheseIndex=$column.columnComment.indexOf("("))
+ #if($parentheseIndex != -1)
+ #set($comment=$column.columnComment.substring(0, $parentheseIndex))
+ #else
+ #set($comment=$column.columnComment)
+ #end
+ #if($column.pk || $javaField == ${subTableFkclassName})
+ #elseif($column.list && $column.htmlType == "input")
+
+
+
+
+
+ #elseif($column.list && $column.htmlType == "inputNumber")
+
+
+
+
+
+ #elseif($column.list && $column.htmlType == "datetime")
+
+
+
+
+
+ #elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" != $column.dictType)
+
+
+
+
+
+
+
+ #elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" == $column.dictType)
+
+
+
+
+
+
+
+ #end
+ #end
+
+ #end
+
+
+
+
+
+
+
diff --git a/huacai-generator/target/classes/vm/vue/v3/index-tree.vue.vm b/huacai-generator/target/classes/vm/vue/v3/index-tree.vue.vm
new file mode 100644
index 0000000..c54d62b
--- /dev/null
+++ b/huacai-generator/target/classes/vm/vue/v3/index-tree.vue.vm
@@ -0,0 +1,474 @@
+
+
+
+#foreach($column in $columns)
+#if($column.query)
+#set($dictType=$column.dictType)
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.htmlType == "input")
+
+
+
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
+
+
+
+
+
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
+
+
+
+
+#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+
+
+
+#end
+#end
+#end
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 展开/折叠
+
+
+
+
+
+#foreach($column in $columns)
+#set($javaField=$column.javaField)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.pk)
+#elseif($column.list && $column.htmlType == "datetime")
+
+
+ {{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}
+
+
+#elseif($column.list && $column.htmlType == "imageUpload")
+
+
+
+
+
+#elseif($column.list && "" != $column.dictType)
+
+
+#if($column.htmlType == "checkbox")
+
+#else
+
+#end
+
+
+#elseif($column.list && "" != $javaField)
+#if(${foreach.index} == 1)
+
+#else
+
+#end
+#end
+#end
+
+
+ 修改
+ 新增
+ 删除
+
+
+
+
+
+
+
+#foreach($column in $columns)
+#set($field=$column.javaField)
+#if($column.insert && !$column.pk)
+#if(($column.usableColumn) || (!$column.superColumn))
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#set($dictType=$column.dictType)
+#if("" != $treeParentCode && $column.javaField == $treeParentCode)
+
+
+
+#elseif($column.htmlType == "input")
+
+
+
+#elseif($column.htmlType == "imageUpload")
+
+
+
+#elseif($column.htmlType == "fileUpload")
+
+
+
+#elseif($column.htmlType == "editor")
+
+
+
+#elseif($column.htmlType == "select" && "" != $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "select" && $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "checkbox" && "" != $dictType)
+
+
+
+ {{dict.label}}
+
+
+
+#elseif($column.htmlType == "checkbox" && $dictType)
+
+
+ 请选择字典生成
+
+
+#elseif($column.htmlType == "radio" && "" != $dictType)
+
+
+ {{dict.label}}
+
+
+#elseif($column.htmlType == "radio" && $dictType)
+
+
+ 请选择字典生成
+
+
+#elseif($column.htmlType == "datetime")
+
+
+
+
+#elseif($column.htmlType == "textarea")
+
+
+
+#end
+#end
+#end
+#end
+
+
+
+
+
+
+
+
+
diff --git a/huacai-generator/target/classes/vm/vue/v3/index.vue.vm b/huacai-generator/target/classes/vm/vue/v3/index.vue.vm
new file mode 100644
index 0000000..8b25665
--- /dev/null
+++ b/huacai-generator/target/classes/vm/vue/v3/index.vue.vm
@@ -0,0 +1,590 @@
+
+
+
+#foreach($column in $columns)
+#if($column.query)
+#set($dictType=$column.dictType)
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.htmlType == "input")
+
+
+
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
+
+
+
+
+
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
+
+
+
+
+#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+
+
+
+#end
+#end
+#end
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出
+
+
+
+
+
+
+#foreach($column in $columns)
+#set($javaField=$column.javaField)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.pk)
+
+#elseif($column.list && $column.htmlType == "datetime")
+
+
+ {{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}
+
+
+#elseif($column.list && $column.htmlType == "imageUpload")
+
+
+
+
+
+#elseif($column.list && "" != $column.dictType)
+
+
+#if($column.htmlType == "checkbox")
+
+#else
+
+#end
+
+
+#elseif($column.list && "" != $javaField)
+
+#end
+#end
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+#foreach($column in $columns)
+#set($field=$column.javaField)
+#if($column.insert && !$column.pk)
+#if(($column.usableColumn) || (!$column.superColumn))
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#set($dictType=$column.dictType)
+#if($column.htmlType == "input")
+
+
+
+#elseif($column.htmlType == "imageUpload")
+
+
+
+#elseif($column.htmlType == "fileUpload")
+
+
+
+#elseif($column.htmlType == "editor")
+
+
+
+#elseif($column.htmlType == "select" && "" != $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "select" && $dictType)
+
+
+
+
+
+#elseif($column.htmlType == "checkbox" && "" != $dictType)
+
+
+
+ {{dict.label}}
+
+
+
+#elseif($column.htmlType == "checkbox" && $dictType)
+
+
+ 请选择字典生成
+
+
+#elseif($column.htmlType == "radio" && "" != $dictType)
+
+
+ {{dict.label}}
+
+
+#elseif($column.htmlType == "radio" && $dictType)
+
+
+ 请选择字典生成
+
+
+#elseif($column.htmlType == "datetime")
+
+
+
+
+#elseif($column.htmlType == "textarea")
+
+
+
+#end
+#end
+#end
+#end
+#if($table.sub)
+ ${subTable.functionName}信息
+
+
+ 添加
+
+
+ 删除
+
+
+
+
+
+#foreach($column in $subTable.columns)
+#set($javaField=$column.javaField)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.pk || $javaField == ${subTableFkclassName})
+#elseif($column.list && $column.htmlType == "input")
+
+
+
+
+
+#elseif($column.list && $column.htmlType == "datetime")
+
+
+
+
+
+
+#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" != $column.dictType)
+
+
+
+
+
+
+
+#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" == $column.dictType)
+
+
+
+
+
+
+
+#end
+#end
+
+#end
+
+
+
+
+
+
+
+
+
diff --git a/huacai-generator/target/classes/vm/xml/mapper.xml.vm b/huacai-generator/target/classes/vm/xml/mapper.xml.vm
new file mode 100644
index 0000000..4233b10
--- /dev/null
+++ b/huacai-generator/target/classes/vm/xml/mapper.xml.vm
@@ -0,0 +1,135 @@
+
+
+
+
+
+#foreach ($column in $columns)
+
+#end
+
+#if($table.sub)
+
+
+
+
+
+
+#foreach ($column in $subTable.columns)
+
+#end
+
+#end
+
+
+ select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end from ${tableName}
+
+
+
+
+
+
+
+ insert into ${tableName}
+
+#foreach($column in $columns)
+#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment)
+ $column.columnName,
+#end
+#end
+
+
+#foreach($column in $columns)
+#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment)
+ #{$column.javaField},
+#end
+#end
+
+
+
+
+ update ${tableName}
+
+#foreach($column in $columns)
+#if($column.columnName != $pkColumn.columnName)
+ $column.columnName = #{$column.javaField},
+#end
+#end
+
+ where ${pkColumn.columnName} = #{${pkColumn.javaField}}
+
+
+
+ delete from ${tableName} where ${pkColumn.columnName} = #{${pkColumn.javaField}}
+
+
+
+ delete from ${tableName} where ${pkColumn.columnName} in
+
+ #{${pkColumn.javaField}}
+
+
+#if($table.sub)
+
+
+ delete from ${subTableName} where ${subTableFkName} in
+
+ #{${subTableFkclassName}}
+
+
+
+
+ delete from ${subTableName} where ${subTableFkName} = #{${subTableFkclassName}}
+
+
+
+ insert into ${subTableName}(#foreach($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end) values
+
+ (#foreach($column in $subTable.columns) #{item.$column.javaField}#if($foreach.count != $subTable.columns.size()),#end#end)
+
+
+#end
+
diff --git a/huacai-quartz/pom.xml b/huacai-quartz/pom.xml
new file mode 100644
index 0000000..728e9e1
--- /dev/null
+++ b/huacai-quartz/pom.xml
@@ -0,0 +1,40 @@
+
+
+
+ huacai
+ com.huacai
+ 3.8.7
+
+ 4.0.0
+
+ huacai-quartz
+
+
+ quartz定时任务
+
+
+
+
+
+
+ org.quartz-scheduler
+ quartz
+
+
+ com.mchange
+ c3p0
+
+
+
+
+
+
+ com.huacai
+ huacai-common
+
+
+
+
+
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/config/ScheduleConfig.java b/huacai-quartz/src/main/java/com/huacai/quartz/config/ScheduleConfig.java
new file mode 100644
index 0000000..9852b95
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/config/ScheduleConfig.java
@@ -0,0 +1,57 @@
+//package com.huacai.quartz.config;
+//
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+//import javax.sql.DataSource;
+//import java.util.Properties;
+//
+///**
+// * 定时任务配置(单机部署建议删除此类和qrtz数据库表,默认走内存会最高效)
+// *
+// * @author huacai
+// */
+//@Configuration
+//public class ScheduleConfig
+//{
+// @Bean
+// public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource)
+// {
+// SchedulerFactoryBean factory = new SchedulerFactoryBean();
+// factory.setDataSource(dataSource);
+//
+// // quartz参数
+// Properties prop = new Properties();
+// prop.put("org.quartz.scheduler.instanceName", "huacaiScheduler");
+// prop.put("org.quartz.scheduler.instanceId", "AUTO");
+// // 线程池配置
+// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
+// prop.put("org.quartz.threadPool.threadCount", "20");
+// prop.put("org.quartz.threadPool.threadPriority", "5");
+// // JobStore配置
+// prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore");
+// // 集群配置
+// prop.put("org.quartz.jobStore.isClustered", "true");
+// prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
+// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "10");
+// prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
+//
+// // sqlserver 启用
+// // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
+// prop.put("org.quartz.jobStore.misfireThreshold", "12000");
+// prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
+// factory.setQuartzProperties(prop);
+//
+// factory.setSchedulerName("huacaiScheduler");
+// // 延时启动
+// factory.setStartupDelay(1);
+// factory.setApplicationContextSchedulerContextKey("applicationContextKey");
+// // 可选,QuartzScheduler
+// // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
+// factory.setOverwriteExistingJobs(true);
+// // 设置自动启动,默认为true
+// factory.setAutoStartup(true);
+//
+// return factory;
+// }
+//}
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/controller/SysJobController.java b/huacai-quartz/src/main/java/com/huacai/quartz/controller/SysJobController.java
new file mode 100644
index 0000000..d11aa3b
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/controller/SysJobController.java
@@ -0,0 +1,235 @@
+package com.huacai.quartz.controller;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.quartz.SchedulerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.huacai.common.annotation.Log;
+import com.huacai.common.constant.Constants;
+import com.huacai.common.core.controller.BaseController;
+import com.huacai.common.core.domain.AjaxResult;
+import com.huacai.common.core.page.TableDataInfo;
+import com.huacai.common.enums.BusinessType;
+import com.huacai.common.exception.job.TaskException;
+import com.huacai.common.utils.StringUtils;
+import com.huacai.common.utils.poi.ExcelUtil;
+import com.huacai.quartz.domain.SysJob;
+import com.huacai.quartz.service.ISysJobService;
+import com.huacai.quartz.util.CronUtils;
+import com.huacai.quartz.util.ScheduleUtils;
+
+/**
+ * 调度任务信息操作处理控制器
+ * 负责定时任务的CRUD、状态切换、立即执行等核心功能接口
+ *
+ * @author huacai
+ */
+@RestController
+@RequestMapping("/monitor/job")
+public class SysJobController extends BaseController
+{
+ @Autowired
+ private ISysJobService jobService;
+
+ /**
+ * 查询定时任务列表
+ * 支持分页和条件过滤,需"monitor:job:list"权限
+ *
+ * @param sysJob 包含查询条件的任务对象
+ * @return 分页后的任务列表数据
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysJob sysJob)
+ {
+ startPage(); // 开启分页(继承自BaseController)
+ List list = jobService.selectJobList(sysJob); // 调用服务层查询列表
+ return getDataTable(list); // 包装成分页响应对象
+ }
+
+ /**
+ * 导出定时任务列表
+ * 将查询结果导出为Excel文件,需"monitor:job:export"权限
+ *
+ * @param response HTTP响应对象
+ * @param sysJob 查询条件
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:export')")
+ @Log(title = "定时任务", businessType = BusinessType.EXPORT) // 记录操作日志
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysJob sysJob)
+ {
+ List list = jobService.selectJobList(sysJob);
+ ExcelUtil util = new ExcelUtil(SysJob.class);
+ util.exportExcel(response, list, "定时任务"); // 导出Excel
+ }
+
+ /**
+ * 获取定时任务详细信息
+ * 根据任务ID查询单条任务详情,需"monitor:job:query"权限
+ *
+ * @param jobId 任务ID
+ * @return 包含任务详情的响应对象
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:query')")
+ @GetMapping(value = "/{jobId}")
+ public AjaxResult getInfo(@PathVariable("jobId") Long jobId)
+ {
+ return success(jobService.selectJobById(jobId)); // 调用服务层查询详情
+ }
+
+ /**
+ * 新增定时任务
+ * 包含严格的参数校验(Cron表达式、调用目标安全性等),需"monitor:job:add"权限
+ *
+ * @param job 待新增的任务对象
+ * @return 新增结果响应
+ * @throws SchedulerException 调度器异常
+ * @throws TaskException 任务处理异常
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:add')")
+ @Log(title = "定时任务", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException
+ {
+ // 校验Cron表达式合法性
+ if (!CronUtils.isValid(job.getCronExpression()))
+ {
+ return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确");
+ }
+ // 安全校验:禁止RMI调用
+ else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
+ {
+ return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用");
+ }
+ // 安全校验:禁止LDAP调用
+ else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS }))
+ {
+ return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用");
+ }
+ // 安全校验:禁止HTTP调用
+ else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
+ {
+ return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用");
+ }
+ // 安全校验:禁止包含违规字符串
+ else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR))
+ {
+ return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规");
+ }
+ // 安全校验:必须在白名单内
+ else if (!ScheduleUtils.whiteList(job.getInvokeTarget()))
+ {
+ return error("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内");
+ }
+
+ job.setCreateBy(getUsername()); // 设置创建人(从当前登录用户获取)
+ return toAjax(jobService.insertJob(job)); // 调用服务层新增任务
+ }
+
+ /**
+ * 修改定时任务
+ * 包含与新增相同的参数校验逻辑,需"monitor:job:edit"权限
+ *
+ * @param job 待修改的任务对象
+ * @return 修改结果响应
+ * @throws SchedulerException 调度器异常
+ * @throws TaskException 任务处理异常
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:edit')")
+ @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException
+ {
+ // 与新增任务相同的校验逻辑(Cron表达式+安全校验)
+ if (!CronUtils.isValid(job.getCronExpression()))
+ {
+ return error("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确");
+ }
+ else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
+ {
+ return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用");
+ }
+ else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS }))
+ {
+ return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用");
+ }
+ else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
+ {
+ return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用");
+ }
+ else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR))
+ {
+ return error("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规");
+ }
+ else if (!ScheduleUtils.whiteList(job.getInvokeTarget()))
+ {
+ return error("修改任务'" + job.getJobName() + "'失败,目标字符串不在白名单内");
+ }
+
+ job.setUpdateBy(getUsername()); // 设置更新人
+ return toAjax(jobService.updateJob(job)); // 调用服务层更新任务
+ }
+
+ /**
+ * 定时任务状态修改
+ * 用于启用/停用定时任务,需"monitor:job:changeStatus"权限
+ *
+ * @param job 包含任务ID和目标状态的对象
+ * @return 状态修改结果响应
+ * @throws SchedulerException 调度器异常
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
+ @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+ @PutMapping("/changeStatus")
+ public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException
+ {
+ SysJob newJob = jobService.selectJobById(job.getJobId()); // 先查询原任务
+ newJob.setStatus(job.getStatus()); // 更新状态
+ return toAjax(jobService.changeStatus(newJob)); // 调用服务层修改状态
+ }
+
+ /**
+ * 定时任务立即执行一次
+ * 无视Cron表达式,立即触发任务执行,需"monitor:job:changeStatus"权限
+ *
+ * @param job 包含任务ID的对象
+ * @return 执行结果响应
+ * @throws SchedulerException 调度器异常
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
+ @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+ @PutMapping("/run")
+ public AjaxResult run(@RequestBody SysJob job) throws SchedulerException
+ {
+ boolean result = jobService.run(job); // 调用服务层立即执行
+ return result ? success() : error("任务不存在或已过期!");
+ }
+
+ /**
+ * 删除定时任务
+ * 批量删除指定ID的任务,需"monitor:job:remove"权限
+ *
+ * @param jobIds 任务ID数组
+ * @return 删除结果响应
+ * @throws SchedulerException 调度器异常
+ * @throws TaskException 任务处理异常
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+ @Log(title = "定时任务", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{jobIds}")
+ public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException
+ {
+ jobService.deleteJobByIds(jobIds); // 调用服务层批量删除
+ return success();
+ }
+}
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/controller/SysJobLogController.java b/huacai-quartz/src/main/java/com/huacai/quartz/controller/SysJobLogController.java
new file mode 100644
index 0000000..cb70a92
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/controller/SysJobLogController.java
@@ -0,0 +1,111 @@
+package com.huacai.quartz.controller;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.huacai.common.annotation.Log;
+import com.huacai.common.core.controller.BaseController;
+import com.huacai.common.core.domain.AjaxResult;
+import com.huacai.common.core.page.TableDataInfo;
+import com.huacai.common.enums.BusinessType;
+import com.huacai.common.utils.poi.ExcelUtil;
+import com.huacai.quartz.domain.SysJobLog;
+import com.huacai.quartz.service.ISysJobLogService;
+
+/**
+ * 调度日志操作处理控制器
+ * 负责定时任务执行日志的查询、导出、删除、清空等管理功能
+ *
+ * @author huacai
+ */
+@RestController
+@RequestMapping("/monitor/jobLog")
+public class SysJobLogController extends BaseController
+{
+ @Autowired
+ private ISysJobLogService jobLogService;
+
+ /**
+ * 查询定时任务调度日志列表
+ * 支持分页和条件过滤,需"monitor:job:list"权限
+ *
+ * @param sysJobLog 包含查询条件的日志对象
+ * @return 分页后的日志列表数据
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysJobLog sysJobLog)
+ {
+ startPage(); // 开启分页(继承自BaseController)
+ List list = jobLogService.selectJobLogList(sysJobLog); // 调用服务层查询日志列表
+ return getDataTable(list); // 包装成分页响应对象
+ }
+
+ /**
+ * 导出定时任务调度日志列表
+ * 将查询结果导出为Excel文件,需"monitor:job:export"权限
+ *
+ * @param response HTTP响应对象
+ * @param sysJobLog 查询条件
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:export')")
+ @Log(title = "任务调度日志", businessType = BusinessType.EXPORT) // 记录导出操作日志
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysJobLog sysJobLog)
+ {
+ List list = jobLogService.selectJobLogList(sysJobLog);
+ ExcelUtil util = new ExcelUtil(SysJobLog.class);
+ util.exportExcel(response, list, "调度日志"); // 导出Excel文件
+ }
+
+ /**
+ * 根据调度日志ID获取详细信息
+ * 查询单条日志的详细执行记录,需"monitor:job:query"权限
+ *
+ * @param jobLogId 日志ID
+ * @return 包含日志详情的响应对象
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:query')")
+ @GetMapping(value = "/{jobLogId}")
+ public AjaxResult getInfo(@PathVariable Long jobLogId)
+ {
+ return success(jobLogService.selectJobLogById(jobLogId)); // 调用服务层查询日志详情
+ }
+
+ /**
+ * 批量删除定时任务调度日志
+ * 删除指定ID的日志记录,需"monitor:job:remove"权限
+ *
+ * @param jobLogIds 日志ID数组
+ * @return 删除结果响应
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+ @Log(title = "定时任务调度日志", businessType = BusinessType.DELETE) // 记录删除操作日志
+ @DeleteMapping("/{jobLogIds}")
+ public AjaxResult remove(@PathVariable Long[] jobLogIds)
+ {
+ return toAjax(jobLogService.deleteJobLogByIds(jobLogIds)); // 调用服务层批量删除
+ }
+
+ /**
+ * 清空所有定时任务调度日志
+ * 一次性删除系统中所有任务调度日志,需"monitor:job:remove"权限
+ *
+ * @return 清空结果响应
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+ @Log(title = "调度日志", businessType = BusinessType.CLEAN) // 记录清空操作日志
+ @DeleteMapping("/clean")
+ public AjaxResult clean()
+ {
+ jobLogService.cleanJobLog(); // 调用服务层清空日志
+ return success();
+ }
+}
\ No newline at end of file
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/domain/SysJob.java b/huacai-quartz/src/main/java/com/huacai/quartz/domain/SysJob.java
new file mode 100644
index 0000000..5f65a02
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/domain/SysJob.java
@@ -0,0 +1,192 @@
+package com.huacai.quartz.domain;
+
+import java.util.Date;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.huacai.common.annotation.Excel;
+import com.huacai.common.annotation.Excel.ColumnType;
+import com.huacai.common.constant.ScheduleConstants;
+import com.huacai.common.core.domain.BaseEntity;
+import com.huacai.common.utils.StringUtils;
+import com.huacai.quartz.util.CronUtils;
+
+/**
+ * 定时任务调度实体类(对应表sys_job)
+ * 封装定时任务的基本信息、调度规则及执行状态
+ *
+ * @author huacai
+ */
+public class SysJob extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 任务ID(主键) */
+ @Excel(name = "任务序号", cellType = ColumnType.NUMERIC) // Excel导出时的配置
+ private Long jobId;
+
+ /** 任务名称 */
+ @Excel(name = "任务名称")
+ private String jobName;
+
+ /** 任务组名(用于分类管理任务) */
+ @Excel(name = "任务组名")
+ private String jobGroup;
+
+ /** 调用目标字符串(任务执行的目标方法,如类名.方法名) */
+ @Excel(name = "调用目标字符串")
+ private String invokeTarget;
+
+ /** cron执行表达式(任务调度的时间规则) */
+ @Excel(name = "执行表达式 ")
+ private String cronExpression;
+
+ /**
+ * cron计划策略(任务错过执行时的处理策略)
+ * 默认值为ScheduleConstants.MISFIRE_DEFAULT(默认策略)
+ */
+ @Excel(name = "计划策略 ", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行")
+ private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;
+
+ /** 是否并发执行(0允许 1禁止) */
+ @Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止")
+ private String concurrent;
+
+ /** 任务状态(0正常 1暂停) */
+ @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停")
+ private String status;
+
+ // ------------------- Getter和Setter方法 -------------------
+ public Long getJobId()
+ {
+ return jobId;
+ }
+
+ public void setJobId(Long jobId)
+ {
+ this.jobId = jobId;
+ }
+
+ /**
+ * 任务名称校验:非空且长度不超过64字符
+ */
+ @NotBlank(message = "任务名称不能为空")
+ @Size(min = 0, max = 64, message = "任务名称不能超过64个字符")
+ public String getJobName()
+ {
+ return jobName;
+ }
+
+ public void setJobName(String jobName)
+ {
+ this.jobName = jobName;
+ }
+
+ public String getJobGroup()
+ {
+ return jobGroup;
+ }
+
+ public void setJobGroup(String jobGroup)
+ {
+ this.jobGroup = jobGroup;
+ }
+
+ /**
+ * 调用目标字符串校验:非空且长度不超过500字符
+ */
+ @NotBlank(message = "调用目标字符串不能为空")
+ @Size(min = 0, max = 500, message = "调用目标字符串长度不能超过500个字符")
+ public String getInvokeTarget()
+ {
+ return invokeTarget;
+ }
+
+ public void setInvokeTarget(String invokeTarget)
+ {
+ this.invokeTarget = invokeTarget;
+ }
+
+ /**
+ * Cron表达式校验:非空且长度不超过255字符
+ */
+ @NotBlank(message = "Cron执行表达式不能为空")
+ @Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符")
+ public String getCronExpression()
+ {
+ return cronExpression;
+ }
+
+ public void setCronExpression(String cronExpression)
+ {
+ this.cronExpression = cronExpression;
+ }
+
+ /**
+ * 计算下次执行时间(通过Cron表达式解析)
+ * 前端展示用,不对应数据库字段
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ public Date getNextValidTime()
+ {
+ if (StringUtils.isNotEmpty(cronExpression))
+ {
+ return CronUtils.getNextExecution(cronExpression);
+ }
+ return null;
+ }
+
+ public String getMisfirePolicy()
+ {
+ return misfirePolicy;
+ }
+
+ public void setMisfirePolicy(String misfirePolicy)
+ {
+ this.misfirePolicy = misfirePolicy;
+ }
+
+ public String getConcurrent()
+ {
+ return concurrent;
+ }
+
+ public void setConcurrent(String concurrent)
+ {
+ this.concurrent = concurrent;
+ }
+
+ public String getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(String status)
+ {
+ this.status = status;
+ }
+
+ /**
+ * 重写toString方法,便于日志打印和调试
+ */
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+ .append("jobId", getJobId())
+ .append("jobName", getJobName())
+ .append("jobGroup", getJobGroup())
+ .append("cronExpression", getCronExpression())
+ .append("nextValidTime", getNextValidTime())
+ .append("misfirePolicy", getMisfirePolicy())
+ .append("concurrent", getConcurrent())
+ .append("status", getStatus())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
\ No newline at end of file
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/domain/SysJobLog.java b/huacai-quartz/src/main/java/com/huacai/quartz/domain/SysJobLog.java
new file mode 100644
index 0000000..d061fd7
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/domain/SysJobLog.java
@@ -0,0 +1,234 @@
+
+package com.huacai.quartz.domain;
+
+import java.util.Date;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.huacai.common.annotation.Excel;
+import com.huacai.common.core.domain.BaseEntity;
+
+/**
+ * 定时任务调度日志表对应的实体类(映射表sys_job_log)
+ * 用于记录定时任务每次执行的详细日志信息,包括执行状态、时间、异常等
+ *
+ * @author huacai
+ */
+public class SysJobLog extends BaseEntity // 继承基础实体类,复用通用字段(如创建时间等)
+{
+ // 序列化版本号,用于对象序列化时的版本控制
+ private static final long serialVersionUID = 1L;
+
+ /** 日志ID(主键) */
+ @Excel(name = "日志序号") // Excel导出时的列名配置
+ private Long jobLogId;
+
+ /** 任务名称(关联sys_job表的jobName) */
+ @Excel(name = "任务名称")
+ private String jobName;
+
+ /** 任务组名(关联sys_job表的jobGroup,用于分类) */
+ @Excel(name = "任务组名")
+ private String jobGroup;
+
+ /** 调用目标字符串(记录任务执行的目标方法,与任务定义一致) */
+ @Excel(name = "调用目标字符串")
+ private String invokeTarget;
+
+ /** 日志信息(记录任务执行的简要描述,如"执行成功") */
+ @Excel(name = "日志信息")
+ private String jobMessage;
+
+ /** 执行状态(0正常 1失败,用于快速标识任务执行结果) */
+ @Excel(name = "执行状态", readConverterExp = "0=正常,1=失败") // 导出时自动转换编码为文字描述
+ private String status;
+
+ /** 异常信息(当执行失败时,记录具体的异常堆栈信息,便于排查问题) */
+ @Excel(name = "异常信息")
+ private String exceptionInfo;
+
+ /** 开始时间(任务实际开始执行的时间) */
+ private Date startTime;
+
+ /** 停止时间(任务实际执行结束的时间) */
+ private Date stopTime;
+
+ /**
+ * 获取日志ID
+ * @return 日志ID
+ */
+ public Long getJobLogId()
+ {
+ return jobLogId;
+ }
+
+ /**
+ * 设置日志ID
+ * @param jobLogId 日志ID
+ */
+ public void setJobLogId(Long jobLogId)
+ {
+ this.jobLogId = jobLogId;
+ }
+
+ /**
+ * 获取任务名称
+ * @return 任务名称
+ */
+ public String getJobName()
+ {
+ return jobName;
+ }
+
+ /**
+ * 设置任务名称
+ * @param jobName 任务名称
+ */
+ public void setJobName(String jobName)
+ {
+ this.jobName = jobName;
+ }
+
+ /**
+ * 获取任务组名
+ * @return 任务组名
+ */
+ public String getJobGroup()
+ {
+ return jobGroup;
+ }
+
+ /**
+ * 设置任务组名
+ * @param jobGroup 任务组名
+ */
+ public void setJobGroup(String jobGroup)
+ {
+ this.jobGroup = jobGroup;
+ }
+
+ /**
+ * 获取调用目标字符串
+ * @return 调用目标字符串
+ */
+ public String getInvokeTarget()
+ {
+ return invokeTarget;
+ }
+
+ /**
+ * 设置调用目标字符串
+ * @param invokeTarget 调用目标字符串
+ */
+ public void setInvokeTarget(String invokeTarget)
+ {
+ this.invokeTarget = invokeTarget;
+ }
+
+ /**
+ * 获取日志信息
+ * @return 日志信息
+ */
+ public String getJobMessage()
+ {
+ return jobMessage;
+ }
+
+ /**
+ * 设置日志信息
+ * @param jobMessage 日志信息
+ */
+ public void setJobMessage(String jobMessage)
+ {
+ this.jobMessage = jobMessage;
+ }
+
+ /**
+ * 获取执行状态(0正常 1失败)
+ * @return 执行状态
+ */
+ public String getStatus()
+ {
+ return status;
+ }
+
+ /**
+ * 设置执行状态(0正常 1失败)
+ * @param status 执行状态
+ */
+ public void setStatus(String status)
+ {
+ this.status = status;
+ }
+
+ /**
+ * 获取异常信息
+ * @return 异常信息
+ */
+ public String getExceptionInfo()
+ {
+ return exceptionInfo;
+ }
+
+ /**
+ * 设置异常信息
+ * @param exceptionInfo 异常信息
+ */
+ public void setExceptionInfo(String exceptionInfo)
+ {
+ this.exceptionInfo = exceptionInfo;
+ }
+
+ /**
+ * 获取开始时间
+ * @return 开始时间
+ */
+ public Date getStartTime()
+ {
+ return startTime;
+ }
+
+ /**
+ * 设置开始时间
+ * @param startTime 开始时间
+ */
+ public void setStartTime(Date startTime)
+ {
+ this.startTime = startTime;
+ }
+
+ /**
+ * 获取停止时间
+ * @return 停止时间
+ */
+ public Date getStopTime()
+ {
+ return stopTime;
+ }
+
+ /**
+ * 设置停止时间
+ * @param stopTime 停止时间
+ */
+ public void setStopTime(Date stopTime)
+ {
+ this.stopTime = stopTime;
+ }
+
+ /**
+ * 重写toString方法,使用多行动格式输出对象信息
+ * 便于日志打印和调试时查看对象详情
+ */
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("jobLogId", getJobLogId())
+ .append("jobName", getJobName())
+ .append("jobGroup", getJobGroup())
+ .append("jobMessage", getJobMessage())
+ .append("status", getStatus())
+ .append("exceptionInfo", getExceptionInfo())
+ .append("startTime", getStartTime())
+ .append("stopTime", getStopTime())
+ .toString();
+ }
+}
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/mapper/SysJobLogMapper.java b/huacai-quartz/src/main/java/com/huacai/quartz/mapper/SysJobLogMapper.java
new file mode 100644
index 0000000..9c4633c
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/mapper/SysJobLogMapper.java
@@ -0,0 +1,64 @@
+package com.huacai.quartz.mapper;
+
+import java.util.List;
+import com.huacai.quartz.domain.SysJobLog;
+
+/**
+ * 调度任务日志信息 数据层
+ *
+ * @author huacai
+ */
+public interface SysJobLogMapper
+{
+ /**
+ * 获取quartz调度器日志的计划任务
+ *
+ * @param jobLog 调度日志信息
+ * @return 调度任务日志集合
+ */
+ public List selectJobLogList(SysJobLog jobLog);
+
+ /**
+ * 查询所有调度任务日志
+ *
+ * @return 调度任务日志列表
+ */
+ public List selectJobLogAll();
+
+ /**
+ * 通过调度任务日志ID查询调度信息
+ *
+ * @param jobLogId 调度任务日志ID
+ * @return 调度任务日志对象信息
+ */
+ public SysJobLog selectJobLogById(Long jobLogId);
+
+ /**
+ * 新增任务日志
+ *
+ * @param jobLog 调度日志信息
+ * @return 结果
+ */
+ public int insertJobLog(SysJobLog jobLog);
+
+ /**
+ * 批量删除调度日志信息
+ *
+ * @param logIds 需要删除的数据ID
+ * @return 结果
+ */
+ public int deleteJobLogByIds(Long[] logIds);
+
+ /**
+ * 删除任务日志
+ *
+ * @param jobId 调度日志ID
+ * @return 结果
+ */
+ public int deleteJobLogById(Long jobId);
+
+ /**
+ * 清空任务日志
+ */
+ public void cleanJobLog();
+}
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/mapper/SysJobMapper.java b/huacai-quartz/src/main/java/com/huacai/quartz/mapper/SysJobMapper.java
new file mode 100644
index 0000000..cf1970e
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/mapper/SysJobMapper.java
@@ -0,0 +1,67 @@
+package com.huacai.quartz.mapper;
+
+import java.util.List;
+import com.huacai.quartz.domain.SysJob;
+
+/**
+ * 调度任务信息 数据层
+ *
+ * @author huacai
+ */
+public interface SysJobMapper
+{
+ /**
+ * 查询调度任务日志集合
+ *
+ * @param job 调度信息
+ * @return 操作日志集合
+ */
+ public List selectJobList(SysJob job);
+
+ /**
+ * 查询所有调度任务
+ *
+ * @return 调度任务列表
+ */
+ public List selectJobAll();
+
+ /**
+ * 通过调度ID查询调度任务信息
+ *
+ * @param jobId 调度ID
+ * @return 角色对象信息
+ */
+ public SysJob selectJobById(Long jobId);
+
+ /**
+ * 通过调度ID删除调度任务信息
+ *
+ * @param jobId 调度ID
+ * @return 结果
+ */
+ public int deleteJobById(Long jobId);
+
+ /**
+ * 批量删除调度任务信息
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ public int deleteJobByIds(Long[] ids);
+
+ /**
+ * 修改调度任务信息
+ *
+ * @param job 调度任务信息
+ * @return 结果
+ */
+ public int updateJob(SysJob job);
+
+ /**
+ * 新增调度任务信息
+ *
+ * @param job 调度任务信息
+ * @return 结果
+ */
+ public int insertJob(SysJob job);
+}
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/service/ISysJobLogService.java b/huacai-quartz/src/main/java/com/huacai/quartz/service/ISysJobLogService.java
new file mode 100644
index 0000000..8c302a1
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/service/ISysJobLogService.java
@@ -0,0 +1,56 @@
+package com.huacai.quartz.service;
+
+import java.util.List;
+import com.huacai.quartz.domain.SysJobLog;
+
+/**
+ * 定时任务调度日志信息信息 服务层
+ *
+ * @author huacai
+ */
+public interface ISysJobLogService
+{
+ /**
+ * 获取quartz调度器日志的计划任务
+ *
+ * @param jobLog 调度日志信息
+ * @return 调度任务日志集合
+ */
+ public List selectJobLogList(SysJobLog jobLog);
+
+ /**
+ * 通过调度任务日志ID查询调度信息
+ *
+ * @param jobLogId 调度任务日志ID
+ * @return 调度任务日志对象信息
+ */
+ public SysJobLog selectJobLogById(Long jobLogId);
+
+ /**
+ * 新增任务日志
+ *
+ * @param jobLog 调度日志信息
+ */
+ public void addJobLog(SysJobLog jobLog);
+
+ /**
+ * 批量删除调度日志信息
+ *
+ * @param logIds 需要删除的日志ID
+ * @return 结果
+ */
+ public int deleteJobLogByIds(Long[] logIds);
+
+ /**
+ * 删除任务日志
+ *
+ * @param jobId 调度日志ID
+ * @return 结果
+ */
+ public int deleteJobLogById(Long jobId);
+
+ /**
+ * 清空任务日志
+ */
+ public void cleanJobLog();
+}
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/service/ISysJobService.java b/huacai-quartz/src/main/java/com/huacai/quartz/service/ISysJobService.java
new file mode 100644
index 0000000..3fabf12
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/service/ISysJobService.java
@@ -0,0 +1,102 @@
+package com.huacai.quartz.service;
+
+import java.util.List;
+import org.quartz.SchedulerException;
+import com.huacai.common.exception.job.TaskException;
+import com.huacai.quartz.domain.SysJob;
+
+/**
+ * 定时任务调度信息信息 服务层
+ *
+ * @author huacai
+ */
+public interface ISysJobService
+{
+ /**
+ * 获取quartz调度器的计划任务
+ *
+ * @param job 调度信息
+ * @return 调度任务集合
+ */
+ public List selectJobList(SysJob job);
+
+ /**
+ * 通过调度任务ID查询调度信息
+ *
+ * @param jobId 调度任务ID
+ * @return 调度任务对象信息
+ */
+ public SysJob selectJobById(Long jobId);
+
+ /**
+ * 暂停任务
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int pauseJob(SysJob job) throws SchedulerException;
+
+ /**
+ * 恢复任务
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int resumeJob(SysJob job) throws SchedulerException;
+
+ /**
+ * 删除任务后,所对应的trigger也将被删除
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int deleteJob(SysJob job) throws SchedulerException;
+
+ /**
+ * 批量删除调度信息
+ *
+ * @param jobIds 需要删除的任务ID
+ * @return 结果
+ */
+ public void deleteJobByIds(Long[] jobIds) throws SchedulerException;
+
+ /**
+ * 任务调度状态修改
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int changeStatus(SysJob job) throws SchedulerException;
+
+ /**
+ * 立即运行任务
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public boolean run(SysJob job) throws SchedulerException;
+
+ /**
+ * 新增任务
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int insertJob(SysJob job) throws SchedulerException, TaskException;
+
+ /**
+ * 更新任务
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int updateJob(SysJob job) throws SchedulerException, TaskException;
+
+ /**
+ * 校验cron表达式是否有效
+ *
+ * @param cronExpression 表达式
+ * @return 结果
+ */
+ public boolean checkCronExpressionIsValid(String cronExpression);
+}
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/service/impl/SysJobLogServiceImpl.java b/huacai-quartz/src/main/java/com/huacai/quartz/service/impl/SysJobLogServiceImpl.java
new file mode 100644
index 0000000..715cc2d
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/service/impl/SysJobLogServiceImpl.java
@@ -0,0 +1,87 @@
+package com.huacai.quartz.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.huacai.quartz.domain.SysJobLog;
+import com.huacai.quartz.mapper.SysJobLogMapper;
+import com.huacai.quartz.service.ISysJobLogService;
+
+/**
+ * 定时任务调度日志信息 服务层
+ *
+ * @author huacai
+ */
+@Service
+public class SysJobLogServiceImpl implements ISysJobLogService
+{
+ @Autowired
+ private SysJobLogMapper jobLogMapper;
+
+ /**
+ * 获取quartz调度器日志的计划任务
+ *
+ * @param jobLog 调度日志信息
+ * @return 调度任务日志集合
+ */
+ @Override
+ public List selectJobLogList(SysJobLog jobLog)
+ {
+ return jobLogMapper.selectJobLogList(jobLog);
+ }
+
+ /**
+ * 通过调度任务日志ID查询调度信息
+ *
+ * @param jobLogId 调度任务日志ID
+ * @return 调度任务日志对象信息
+ */
+ @Override
+ public SysJobLog selectJobLogById(Long jobLogId)
+ {
+ return jobLogMapper.selectJobLogById(jobLogId);
+ }
+
+ /**
+ * 新增任务日志
+ *
+ * @param jobLog 调度日志信息
+ */
+ @Override
+ public void addJobLog(SysJobLog jobLog)
+ {
+ jobLogMapper.insertJobLog(jobLog);
+ }
+
+ /**
+ * 批量删除调度日志信息
+ *
+ * @param logIds 需要删除的数据ID
+ * @return 结果
+ */
+ @Override
+ public int deleteJobLogByIds(Long[] logIds)
+ {
+ return jobLogMapper.deleteJobLogByIds(logIds);
+ }
+
+ /**
+ * 删除任务日志
+ *
+ * @param jobId 调度日志ID
+ */
+ @Override
+ public int deleteJobLogById(Long jobId)
+ {
+ return jobLogMapper.deleteJobLogById(jobId);
+ }
+
+ /**
+ * 清空任务日志
+ */
+ @Override
+ public void cleanJobLog()
+ {
+ jobLogMapper.cleanJobLog();
+ }
+}
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/service/impl/SysJobServiceImpl.java b/huacai-quartz/src/main/java/com/huacai/quartz/service/impl/SysJobServiceImpl.java
new file mode 100644
index 0000000..624ccf6
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/service/impl/SysJobServiceImpl.java
@@ -0,0 +1,296 @@
+package com.huacai.quartz.service.impl;
+
+import java.util.List;
+import javax.annotation.PostConstruct;
+import org.quartz.JobDataMap;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import com.huacai.common.constant.ScheduleConstants;
+import com.huacai.common.exception.job.TaskException;
+import com.huacai.quartz.domain.SysJob;
+import com.huacai.quartz.mapper.SysJobMapper;
+import com.huacai.quartz.service.ISysJobService;
+import com.huacai.quartz.util.CronUtils;
+import com.huacai.quartz.util.ScheduleUtils;
+
+/**
+ * 定时任务调度信息服务层实现类
+ * 负责定时任务的CRUD、状态管理、调度控制等核心业务逻辑
+ *
+ * @author huacai
+ */
+@Service // 标识为Spring服务组件
+public class SysJobServiceImpl implements ISysJobService // 实现定时任务服务接口
+{
+ @Autowired // 自动注入Quartz调度器实例
+ private Scheduler scheduler;
+
+ @Autowired // 自动注入任务数据访问层接口
+ private SysJobMapper jobMapper;
+
+ /**
+ * 项目启动时初始化定时器
+ * 作用:将数据库中配置的任务同步到Quartz调度器中
+ * 注意:禁止手动修改数据库中的任务ID和任务组名,否则会导致数据不一致
+ * @throws SchedulerException Quartz调度器异常
+ * @throws TaskException 任务处理异常
+ */
+ @PostConstruct // 标注此方法在Bean初始化后自动执行
+ public void init() throws SchedulerException, TaskException
+ {
+ scheduler.clear(); // 清空调度器中已有的任务(防止重复加载)
+ List jobList = jobMapper.selectJobAll(); // 查询数据库中所有任务配置
+ for (SysJob job : jobList)
+ {
+ // 为每个任务创建Quartz调度任务
+ ScheduleUtils.createScheduleJob(scheduler, job);
+ }
+ }
+
+ /**
+ * 获取quartz调度器的计划任务列表
+ * 支持根据条件查询任务
+ * @param job 包含查询条件的任务对象
+ * @return 符合条件的任务列表
+ */
+ @Override
+ public List selectJobList(SysJob job)
+ {
+ // 调用Mapper层查询任务列表
+ return jobMapper.selectJobList(job);
+ }
+
+ /**
+ * 通过调度任务ID查询调度信息
+ * @param jobId 调度任务ID
+ * @return 调度任务对象信息
+ */
+ @Override
+ public SysJob selectJobById(Long jobId)
+ {
+ // 调用Mapper层根据ID查询任务
+ return jobMapper.selectJobById(jobId);
+ }
+
+ /**
+ * 暂停任务
+ * 同时更新数据库状态和Quartz调度器中的任务状态
+ * @param job 调度信息
+ * @return 影响的行数
+ * @throws SchedulerException Quartz调度器异常
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class) // 声明事务,发生异常时回滚
+ public int pauseJob(SysJob job) throws SchedulerException
+ {
+ Long jobId = job.getJobId(); // 获取任务ID
+ String jobGroup = job.getJobGroup(); // 获取任务组名
+ job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); // 设置任务状态为"暂停"
+ int rows = jobMapper.updateJob(job); // 更新数据库中的任务状态
+ if (rows > 0) // 数据库更新成功后,同步更新调度器中的任务状态
+ {
+ // 暂停Quartz中的任务
+ scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+ }
+ return rows; // 返回数据库更新影响的行数
+ }
+
+ /**
+ * 恢复任务
+ * 同时更新数据库状态和Quartz调度器中的任务状态
+ * @param job 调度信息
+ * @return 影响的行数
+ * @throws SchedulerException Quartz调度器异常
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int resumeJob(SysJob job) throws SchedulerException
+ {
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ job.setStatus(ScheduleConstants.Status.NORMAL.getValue()); // 设置任务状态为"正常"
+ int rows = jobMapper.updateJob(job); // 更新数据库状态
+ if (rows > 0) // 同步更新调度器状态
+ {
+ // 恢复Quartz中的任务
+ scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+ }
+ return rows;
+ }
+
+ /**
+ * 删除任务
+ * 同时删除数据库记录和Quartz调度器中的任务(包括关联的trigger)
+ * @param job 调度信息
+ * @return 影响的行数
+ * @throws SchedulerException Quartz调度器异常
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int deleteJob(SysJob job) throws SchedulerException
+ {
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ int rows = jobMapper.deleteJobById(jobId); // 从数据库删除任务
+ if (rows > 0) // 同步删除调度器中的任务
+ {
+ // 删除Quartz中的任务(会自动删除关联的trigger)
+ scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+ }
+ return rows;
+ }
+
+ /**
+ * 批量删除调度信息
+ * @param jobIds 需要删除的任务ID数组
+ * @throws SchedulerException Quartz调度器异常
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteJobByIds(Long[] jobIds) throws SchedulerException
+ {
+ for (Long jobId : jobIds) // 遍历每个任务ID
+ {
+ SysJob job = jobMapper.selectJobById(jobId); // 查询任务详情
+ deleteJob(job); // 调用单条删除方法
+ }
+ }
+
+ /**
+ * 任务调度状态修改(切换启用/暂停状态)
+ * @param job 调度信息(包含目标状态)
+ * @return 影响的行数
+ * @throws SchedulerException Quartz调度器异常
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int changeStatus(SysJob job) throws SchedulerException
+ {
+ int rows = 0;
+ String status = job.getStatus(); // 获取目标状态
+ if (ScheduleConstants.Status.NORMAL.getValue().equals(status))
+ {
+ // 目标状态为"正常",执行恢复任务操作
+ rows = resumeJob(job);
+ }
+ else if (ScheduleConstants.Status.PAUSE.getValue().equals(status))
+ {
+ // 目标状态为"暂停",执行暂停任务操作
+ rows = pauseJob(job);
+ }
+ return rows;
+ }
+
+ /**
+ * 立即运行任务(无视Cron表达式,触发一次执行)
+ * @param job 调度信息
+ * @return 执行结果(true表示成功,false表示任务不存在)
+ * @throws SchedulerException Quartz调度器异常
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean run(SysJob job) throws SchedulerException
+ {
+ boolean result = false;
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ // 查询任务完整信息(包含最新配置)
+ SysJob properties = selectJobById(job.getJobId());
+ // 构建任务参数映射(用于传递任务信息到执行器)
+ JobDataMap dataMap = new JobDataMap();
+ dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
+ // 获取Quartz中的任务标识
+ JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
+ // 检查任务是否存在
+ if (scheduler.checkExists(jobKey))
+ {
+ result = true;
+ // 立即触发任务执行,并传递参数
+ scheduler.triggerJob(jobKey, dataMap);
+ }
+ return result;
+ }
+
+ /**
+ * 新增任务
+ * 同时保存到数据库和Quartz调度器
+ * @param job 调度信息
+ * @return 影响的行数
+ * @throws SchedulerException Quartz调度器异常
+ * @throws TaskException 任务处理异常(如Cron表达式无效)
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int insertJob(SysJob job) throws SchedulerException, TaskException
+ {
+ // 新任务默认状态为"暂停"(需手动启用)
+ job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
+ int rows = jobMapper.insertJob(job); // 保存到数据库
+ if (rows > 0) // 数据库保存成功后,同步到调度器
+ {
+ // 创建Quartz调度任务
+ ScheduleUtils.createScheduleJob(scheduler, job);
+ }
+ return rows;
+ }
+
+ /**
+ * 更新任务的配置信息(包括时间表达式等)
+ * @param job 调度信息(包含更新后的配置)
+ * @return 影响的行数
+ * @throws SchedulerException Quartz调度器异常
+ * @throws TaskException 任务处理异常
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int updateJob(SysJob job) throws SchedulerException, TaskException
+ {
+ // 查询数据库中当前任务的旧配置(主要获取旧的任务组名)
+ SysJob properties = selectJobById(job.getJobId());
+ int rows = jobMapper.updateJob(job); // 更新数据库配置
+ if (rows > 0) // 数据库更新成功后,同步更新调度器
+ {
+ // 更新Quartz中的任务配置(使用旧的任务组名进行定位)
+ updateSchedulerJob(job, properties.getJobGroup());
+ }
+ return rows;
+ }
+
+ /**
+ * 内部方法:更新Quartz调度器中的任务配置
+ * @param job 包含新配置的任务对象
+ * @param jobGroup 旧的任务组名(用于定位原有任务)
+ * @throws SchedulerException Quartz调度器异常
+ * @throws TaskException 任务处理异常
+ */
+ public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException
+ {
+ Long jobId = job.getJobId();
+ // 获取原有任务的标识
+ JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
+ // 检查任务是否存在
+ if (scheduler.checkExists(jobKey))
+ {
+ // 先删除原有任务(避免配置冲突)
+ scheduler.deleteJob(jobKey);
+ }
+ // 基于新配置创建任务
+ ScheduleUtils.createScheduleJob(scheduler, job);
+ }
+
+ /**
+ * 校验cron表达式是否有效
+ * @param cronExpression 待校验的Cron表达式
+ * @return 校验结果(true表示有效,false表示无效)
+ */
+ @Override
+ public boolean checkCronExpressionIsValid(String cronExpression)
+ {
+ // 调用工具类进行Cron表达式校验
+ return CronUtils.isValid(cronExpression);
+ }
+}
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/task/RyTask.java b/huacai-quartz/src/main/java/com/huacai/quartz/task/RyTask.java
new file mode 100644
index 0000000..10dabab
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/task/RyTask.java
@@ -0,0 +1,37 @@
+package com.huacai.quartz.task;
+
+import org.springframework.stereotype.Component;
+import com.huacai.common.utils.StringUtils;
+
+/**
+ * 定时任务调度
+ *
+ * @author huacai
+ * @optimization 郑长川(花菜菜)
+ */
+@Component("ryTask")
+public class RyTask
+{
+// @Resource
+// private IJdzsService jdzsService;
+//
+// public void selectJdzs(String jdzsId) {
+// Jdzs jdzs = jdzsService.selectJdzsByJdzsId(jdzsId);
+// System.out.println(jdzs);
+// }
+
+ public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i)
+ {
+ System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i));
+ }
+
+ public void ryParams(String params)
+ {
+ System.out.println("执行有参方法:" + params);
+ }
+
+ public void ryNoParams()
+ {
+ System.out.println("执行无参方法");
+ }
+}
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/util/AbstractQuartzJob.java b/huacai-quartz/src/main/java/com/huacai/quartz/util/AbstractQuartzJob.java
new file mode 100644
index 0000000..d24cba0
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/util/AbstractQuartzJob.java
@@ -0,0 +1,141 @@
+package com.huacai.quartz.util;
+
+import java.util.Date;
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.huacai.common.constant.Constants;
+import com.huacai.common.constant.ScheduleConstants;
+import com.huacai.common.utils.ExceptionUtil;
+import com.huacai.common.utils.StringUtils;
+import com.huacai.common.utils.bean.BeanUtils;
+import com.huacai.common.utils.spring.SpringUtils;
+import com.huacai.quartz.domain.SysJob;
+import com.huacai.quartz.domain.SysJobLog;
+import com.huacai.quartz.service.ISysJobLogService;
+
+/**
+ * 抽象quartz任务执行的抽象基类
+ * 封装任务执行的通用流程(前置处理、后置处理、日志记录等),具体任务逻辑由子类实现
+ *
+ * @author huacai
+ */
+public abstract class AbstractQuartzJob implements Job // 实现Quartz的Job接口,成为可调度任务
+{
+ // 日志记录器,用于记录任务执行过程中的日志信息
+ private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);
+
+ /**
+ * 线程本地变量
+ * 用于存储当前线程中任务的开始时间,避免多线程环境下的时间混乱
+ */
+ private static ThreadLocal threadLocal = new ThreadLocal<>();
+
+ /**
+ * 任务执行的入口方法(实现Job接口的execute方法)
+ * 定义任务执行的完整流程:前置处理 -> 核心执行 -> 后置处理(含异常处理)
+ *
+ * @param context Quartz的任务执行上下文,包含任务相关的环境信息
+ * @throws JobExecutionException 任务执行异常
+ */
+ @Override
+ public void execute(JobExecutionContext context) throws JobExecutionException
+ {
+ // 创建任务对象,用于接收上下文传递的任务配置
+ SysJob sysJob = new SysJob();
+ // 从上下文的参数映射中获取任务配置,并复制到sysJob对象
+ // ScheduleConstants.TASK_PROPERTIES是存储任务配置的键
+ BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES));
+ try
+ {
+ // 执行前置处理(如记录开始时间)
+ before(context, sysJob);
+ // 若任务配置有效,则执行核心任务逻辑
+ if (sysJob != null)
+ {
+ doExecute(context, sysJob); // 抽象方法,由子类实现具体业务
+ }
+ // 执行后置处理(如记录成功日志),无异常
+ after(context, sysJob, null);
+ }
+ catch (Exception e)
+ {
+ // 记录任务执行异常日志
+ log.error("任务执行异常 - :", e);
+ // 执行后置处理(如记录失败日志),传入异常对象
+ after(context, sysJob, e);
+ }
+ }
+
+ /**
+ * 任务执行前的前置处理
+ * 目前主要功能:记录任务开始时间到线程本地变量
+ *
+ * @param context 任务执行上下文
+ * @param sysJob 系统计划任务配置
+ */
+ protected void before(JobExecutionContext context, SysJob sysJob)
+ {
+ // 存储当前时间(任务开始时间)到线程本地变量
+ threadLocal.set(new Date());
+ }
+
+ /**
+ * 任务执行后的后置处理
+ * 主要功能:记录任务执行日志(包括执行时间、耗时、状态、异常信息等)
+ *
+ * @param context 任务执行上下文
+ * @param sysJob 系统计划任务配置
+ * @param e 执行过程中抛出的异常(无异常则为null)
+ */
+ protected void after(JobExecutionContext context, SysJob sysJob, Exception e)
+ {
+ // 从线程本地变量获取任务开始时间
+ Date startTime = threadLocal.get();
+ // 移除线程本地变量中的值,避免内存泄漏
+ threadLocal.remove();
+
+ // 创建任务日志对象,记录本次执行详情
+ final SysJobLog sysJobLog = new SysJobLog();
+ // 设置日志的基本信息(关联的任务名称、组名、调用目标)
+ sysJobLog.setJobName(sysJob.getJobName());
+ sysJobLog.setJobGroup(sysJob.getJobGroup());
+ sysJobLog.setInvokeTarget(sysJob.getInvokeTarget());
+ // 设置开始时间和结束时间
+ sysJobLog.setStartTime(startTime);
+ sysJobLog.setStopTime(new Date());
+ // 计算任务执行耗时(毫秒)
+ long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime();
+ sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒");
+
+ // 根据是否有异常设置任务执行状态
+ if (e != null)
+ {
+ // 有异常:状态为失败(Constants.FAIL)
+ sysJobLog.setStatus(Constants.FAIL);
+ // 获取异常信息并截断(避免日志过长)
+ String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000);
+ sysJobLog.setExceptionInfo(errorMsg);
+ }
+ else
+ {
+ // 无异常:状态为成功(Constants.SUCCESS)
+ sysJobLog.setStatus(Constants.SUCCESS);
+ }
+
+ // 通过Spring工具类获取日志服务Bean,将日志写入数据库
+ SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
+ }
+
+ /**
+ * 任务执行的核心方法,由子类具体实现
+ * 子类需根据业务需求重写此方法,实现实际的任务逻辑
+ *
+ * @param context 任务执行上下文
+ * @param sysJob 系统计划任务配置
+ * @throws Exception 执行过程中可能抛出的异常
+ */
+ protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
+}
\ No newline at end of file
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/util/CronUtils.java b/huacai-quartz/src/main/java/com/huacai/quartz/util/CronUtils.java
new file mode 100644
index 0000000..0c52cd4
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/util/CronUtils.java
@@ -0,0 +1,73 @@
+package com.huacai.quartz.util;
+
+import java.text.ParseException;
+import java.util.Date;
+import org.quartz.CronExpression;
+
+/**
+ * cron表达式工具类
+ * 提供Cron表达式的有效性校验、错误信息获取及下次执行时间计算等功能
+ *
+ * @author huacai
+ *
+ */
+public class CronUtils
+{
+ /**
+ * 校验给定的Cron表达式是否有效
+ * Cron表达式是Quartz定时任务的时间规则表达式,格式错误会导致任务无法调度
+ *
+ * @param cronExpression 需要校验的Cron表达式字符串
+ * @return boolean 表达式有效返回true,无效返回false
+ */
+ public static boolean isValid(String cronExpression)
+ {
+ // 调用Quartz的CronExpression工具类进行有效性校验
+ return CronExpression.isValidExpression(cronExpression);
+ }
+
+ /**
+ * 获取Cron表达式的无效原因描述
+ * 当表达式无效时,返回具体的错误信息(如格式错误位置),便于问题排查
+ *
+ * @param cronExpression 需要校验的Cron表达式字符串
+ * @return String 无效时返回错误描述,有效时返回null
+ */
+ public static String getInvalidMessage(String cronExpression)
+ {
+ try
+ {
+ // 尝试创建CronExpression对象,若成功则表达式有效
+ new CronExpression(cronExpression);
+ return null; // 有效,返回null
+ }
+ catch (ParseException pe)
+ {
+ // 解析失败,返回异常信息(包含具体错误原因)
+ return pe.getMessage();
+ }
+ }
+
+ /**
+ * 根据给定的Cron表达式计算下一次执行时间
+ * 从当前系统时间开始,计算符合Cron规则的最近一次执行时间
+ *
+ * @param cronExpression Cron表达式字符串
+ * @return Date 下次执行时间对象,若表达式无效则抛出异常
+ */
+ public static Date getNextExecution(String cronExpression)
+ {
+ try
+ {
+ // 创建CronExpression对象(已隐含表达式有效性校验)
+ CronExpression cron = new CronExpression(cronExpression);
+ // 计算当前时间之后的下一个有效执行时间
+ return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
+ }
+ catch (ParseException e)
+ {
+ // 表达式解析失败时,包装为运行时异常抛出
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+}
diff --git a/huacai-quartz/src/main/java/com/huacai/quartz/util/JobInvokeUtil.java b/huacai-quartz/src/main/java/com/huacai/quartz/util/JobInvokeUtil.java
new file mode 100644
index 0000000..7bd28df
--- /dev/null
+++ b/huacai-quartz/src/main/java/com/huacai/quartz/util/JobInvokeUtil.java
@@ -0,0 +1,238 @@
+package com.huacai.quartz.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
+import com.huacai.common.utils.StringUtils;
+import com.huacai.common.utils.spring.SpringUtils;
+import com.huacai.quartz.domain.SysJob;
+
+/**
+ * 任务执行工具类
+ * 负责解析定时任务的调用目标(invokeTarget),通过反射机制执行目标方法
+ * 支持Spring容器中的Bean方法和普通Java类的方法调用
+ *
+ * @author huacai
+ */
+public class JobInvokeUtil
+{
+ /**
+ * 执行定时任务的目标方法
+ * 根据任务配置中的invokeTarget解析出Bean名称、方法名和参数,通过反射执行
+ *
+ * @param sysJob 系统任务对象,包含调用目标信息(invokeTarget)
+ * @throws Exception 执行过程中可能抛出的异常(如类找不到、方法不存在等)
+ */
+ public static void invokeMethod(SysJob sysJob) throws Exception
+ {
+ // 获取任务配置的调用目标字符串(格式如:"beanName.methodName(param1, param2)")
+ String invokeTarget = sysJob.getInvokeTarget();
+ // 解析出Bean名称(或类全名)
+ String beanName = getBeanName(invokeTarget);
+ // 解析出方法名称
+ String methodName = getMethodName(invokeTarget);
+ // 解析出方法参数列表(包含参数值和参数类型)
+ List