feat()规则引擎

dev-1
王熙朝 2024-05-06 08:37:15 +08:00
parent 7d880faa7f
commit f0ef7133ea
20 changed files with 593 additions and 19 deletions

View File

@ -63,7 +63,7 @@
<select id="selectDbTableColumnsByName" parameterType="String" resultMap="GenTableColumnResult">
select column_name,
(case when (is_nullable = 'no' <![CDATA[ && ]]> column_key != 'PRI') then '1' else null end) as is_required,
(case when (is_nullable = 'stream' <![CDATA[ && ]]> column_key != 'PRI') then '1' else null end) as is_required,
(case when column_key = 'PRI' then '1' else '0' end) as is_pk,
ordinal_position as sort,
column_comment,

View File

@ -0,0 +1,23 @@
package com.muyu.goods.constant;
/**
*
* @ClassName ConfigCodeConstants
* @Author
* @Date 2024/5/4 15:28
*/
public class ConfigCodeConstants {
/**
*
*/
// public final static String BASE_FILE_PATH="D:/workspace/gtl-ruoyi-server/ruoyi-modules/ruoyi-rule_engine/ruoyi-rule_engine-server/src/main/java/com/ruoyi/ruleEngine/scope/";
public final static String BASE_FILE_PATH="D:\\woster\\gs1\\assets-ser\\muyu-modules\\muyu-goods\\src\\main\\java\\com\\muyu\\goods\\scope\\";
/**
*
*/
public final static String[] CONFIG_FILE_NAME_ARRAY=new String[]{"TestClass.txt","TaskContextHolder.java","DataSetContextHolder.java","RecordContextHolder.java","DataModelContextHolder.java"};
/**
*
*/
public final static String[] CONFIG_FILE_TYPE_ARRAY=new String[]{"测试模版","任务","数据集","资产记录","资产模型"};
}

View File

@ -0,0 +1,22 @@
package com.muyu.goods.constant;
/**
*
* @ClassName RuleOperationConstants
* @Author
* @Date 2024/5/4 16:12
*/
public class RuleOperationConstants {
/**
*
*/
public final static String CLASS_NAME="TestClass";
/**
*
*/
public final static String FILE_SUFFIX=".java";
/**
*
*/
public final static String METHOD_NAME="ruleTest";
}

View File

@ -2,6 +2,9 @@ package com.muyu.goods.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.muyu.goods.domain.model.TextData;
import com.muyu.goods.domain.resp.EngineConfigResp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@ -46,6 +49,17 @@ public class ConfigController extends BaseController
return getDataTable(list);
}
/**
*
* @param ruleId
* @return
*/
@PostMapping("/listConfigEs/{ruleId}")
public Result<List<Config>> listConfigEs(@PathVariable Long ruleId){
List<Config> list = configService.listConfigEs(ruleId);
return success(list);
}
/**
*
*/
@ -101,4 +115,24 @@ public class ConfigController extends BaseController
{
return toAjax(configService.deleteConfigByIds(ids));
}
/**
*
* @param id
* @return
*/
@PostMapping("/testData")
public Result<Object> ruleText(@RequestBody TextData textData){
return success(configService.testData(textData));
}
/**
*
* @param id
* @return
*/
@PostMapping("getScopeInfo/{id}")
public Result<EngineConfigResp> getScopeInfo(@PathVariable Integer id) {
return success(configService.getScopeInfo(id));
}
}

View File

@ -26,6 +26,10 @@ public class Config extends BaseEntity
@Excel(name = "规则内容")
private String ruleContent;
/** 注释 */
@Excel(name = "注释")
private String remark;
/** 维护编号 */
@Excel(name = "维护编号")
private Long ruleId;
@ -57,6 +61,17 @@ public class Config extends BaseEntity
{
return ruleContent;
}
@Override
public String getRemark() {
return remark;
}
@Override
public void setRemark(String remark) {
this.remark = remark;
}
public void setRuleId(Long ruleId)
{
this.ruleId = ruleId;
@ -73,6 +88,7 @@ public class Config extends BaseEntity
.append("id", getId())
.append("versionCode", getVersionCode())
.append("ruleContent", getRuleContent())
.append("remark", getRemark())
.append("ruleId", getRuleId())
.toString();
}

View File

@ -0,0 +1,18 @@
package com.muyu.goods.domain.model;
import lombok.Data;
import java.util.List;
@Data
public class TextData {
/**
*
*/
private Long id;
/**
*
*/
private List<String> list;
}

View File

@ -0,0 +1,19 @@
package com.muyu.goods.domain.resp;
import lombok.Data;
import lombok.experimental.SuperBuilder;
@Data
@SuperBuilder
public class EngineConfigResp {
private static final long serialVersionUID = 1L;
/** 类型 */
private String type;
/** 名称 */
private String name;
/** 代码 */
private String code;
}

View File

@ -0,0 +1,77 @@
package com.muyu.goods.dymamicLoad;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName DynamicLoader
* @Author
* @Date 2024/5/1 20:37
*/
public class DynamicLoader {
/**
* JavaMap
* Map便
*
* @param javaName
* @return map
*/
public static Map<String, byte[]> compile(String javaName, String javaSrc) {
// 调用java编译器接口
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager stdManager = compiler
.getStandardFileManager(null, null, null);
try (MemoryJavaFileManager manager = new MemoryJavaFileManager(
stdManager)) {
@SuppressWarnings("static-access")
JavaFileObject javaFileObject = manager.makeStringSource(javaName,
javaSrc);
JavaCompiler.CompilationTask task = compiler.getTask(null, manager,
null, null, null, Arrays.asList(javaFileObject));
if (task.call()) {
return manager.getClassBytes();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* URLClassLoader defineClass
* URLClassLoaderclassjvm
*
* @author Administrator
*
*/
public static class MemoryClassLoader extends URLClassLoader {
Map<String, byte[]> classBytes = new HashMap<String, byte[]>();
public MemoryClassLoader(Map<String, byte[]> classBytes) {
super(new URL[0], MemoryClassLoader.class.getClassLoader());
this.classBytes.putAll(classBytes);
}
@Override
protected Class<?> findClass(String name)
throws ClassNotFoundException {
byte[] buf = classBytes.get(name);
if (buf == null) {
return super.findClass(name);
}
classBytes.remove(name);
return defineClass(name, buf, 0, buf.length);
}
}
}

View File

@ -0,0 +1,134 @@
package com.muyu.goods.dymamicLoad;
import javax.tools.*;
import java.io.*;
import java.net.URI;
import java.nio.CharBuffer;
import java.util.HashMap;
import java.util.Map;
/**
* .classmap
* @ClassName MemoryJavaFileManager
* @Author
* @Date 2024/5/1 20:38
*/
@SuppressWarnings("rawtypes")
public final class MemoryJavaFileManager extends ForwardingJavaFileManager {
private final static String EXT = ".java";// Java源文件的扩展名
private Map<String, byte[]> classBytes;// 用于存放.class文件的内存
@SuppressWarnings("unchecked")
public MemoryJavaFileManager(JavaFileManager fileManager) {
super(fileManager);
classBytes = new HashMap<String, byte[]>();
}
public Map<String, byte[]> getClassBytes() {
return classBytes;
}
@Override
public void close() throws IOException {
classBytes = new HashMap<String, byte[]>();
}
@Override
public void flush() throws IOException {
}
/**
* stringsourcejkd
*/
private static class StringInputBuffer extends SimpleJavaFileObject {
// The source code of this "file".
final String code;
/**
* Constructs a new JavaSourceFromString.
*
* @param name name
* @param code sourcecode
*/
StringInputBuffer(String name, String code) {
super(toURI(name), Kind.SOURCE);
this.code = code;
}
@Override
public CharBuffer getCharContent(boolean ignoreEncodingErrors) {
return CharBuffer.wrap(code);
}
@SuppressWarnings("unused")
public Reader openReader() {
return new StringReader(code);
}
}
/**
* JavaclassBytes
*/
private class ClassOutputBuffer extends SimpleJavaFileObject {
private String name;
/**
* @param name className
*/
ClassOutputBuffer(String name) {
super(toURI(name), Kind.CLASS);
this.name = name;
}
@Override
public OutputStream openOutputStream() {
return new FilterOutputStream(new ByteArrayOutputStream()) {
@Override
public void close() throws IOException {
out.close();
ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
// 这里需要修改
classBytes.put(name, bos.toByteArray());
}
};
}
}
@Override
public JavaFileObject getJavaFileForOutput(
Location location, String className,
JavaFileObject.Kind kind, FileObject sibling) throws IOException {
if (kind == JavaFileObject.Kind.CLASS) {
return new ClassOutputBuffer(className);
} else {
return super.getJavaFileForOutput(location, className, kind,
sibling);
}
}
static JavaFileObject makeStringSource(String name, String code) {
return new StringInputBuffer(name, code);
}
static URI toURI(String name) {
File file = new File(name);
if (file.exists()) {// 如果文件存在返回他的URI
return file.toURI();
} else {
try {
final StringBuilder newUri = new StringBuilder();
newUri.append("mfm:///");
newUri.append(name.replace('.', '/'));
if (name.endsWith(EXT)) {
newUri.replace(newUri.length() - EXT.length(),
newUri.length(), EXT);
}
return URI.create(newUri.toString());
} catch (Exception exp) {
return URI.create("mfm:///com/sun/script/java/java_source");
}
}
}
}

View File

@ -5,15 +5,15 @@ import com.muyu.goods.domain.Config;
/**
* Mapper
*
*
* @author muyu
* @date 2024-05-04
*/
public interface ConfigMapper
public interface ConfigMapper
{
/**
*
*
*
* @param id
* @return
*/
@ -21,7 +21,7 @@ public interface ConfigMapper
/**
*
*
*
* @param config
* @return
*/
@ -29,7 +29,7 @@ public interface ConfigMapper
/**
*
*
*
* @param config
* @return
*/
@ -37,7 +37,7 @@ public interface ConfigMapper
/**
*
*
*
* @param config
* @return
*/
@ -45,7 +45,7 @@ public interface ConfigMapper
/**
*
*
*
* @param id
* @return
*/
@ -53,9 +53,11 @@ public interface ConfigMapper
/**
*
*
*
* @param ids
* @return
*/
public int deleteConfigByIds(Long[] ids);
List<Config> listConfigEs(Long ruleId);
}

View File

@ -0,0 +1,19 @@
package com.muyu.goods.scope;
import lombok.Data;
/**
*
* @ClassName DataModelContextHolder
* @Author
* @Version: 1.0
*/
@Data
public class DataModelContextHolder {
private final RecordContextHolder recordContextHolder;
public static DataModelContextHolder build(RecordContextHolder recordContextHolder){
return new DataModelContextHolder(recordContextHolder);
}
}

View File

@ -0,0 +1,19 @@
package com.muyu.goods.scope;
import lombok.Data;
/**
*
* @ClassName DataSetContextHolder
* @Author
* @Version: 1.0
*/
@Data
public class DataSetContextHolder {
private final TaskContextHolder taskContextHolder;
public static DataSetContextHolder build(TaskContextHolder taskContextHolder){
return new DataSetContextHolder(taskContextHolder);
}
}

View File

@ -0,0 +1,19 @@
package com.muyu.goods.scope;
import lombok.Data;
/**
*
* @ClassName RecordContextHolder
* @Author
* @Version: 1.0
*/
@Data
public class RecordContextHolder {
private final DataSetContextHolder dataSetContextHolder;
public static RecordContextHolder build(DataSetContextHolder dataSetContextHolder){
return new RecordContextHolder(dataSetContextHolder);
}
}

View File

@ -0,0 +1,14 @@
package com.muyu.goods.scope;
/**
*
* @ClassName TaskContextHolder
* @Author
* @Version: 1.0
*/
public class TaskContextHolder {
public static TaskContextHolder build(){
return new TaskContextHolder();
}
}

View File

@ -0,0 +1,7 @@
import java.util.List;
public class TestClass {
public static String ruleTest(List<String> list) {
return "1";
}
}

View File

@ -2,18 +2,20 @@ package com.muyu.goods.service;
import java.util.List;
import com.muyu.goods.domain.Config;
import com.muyu.goods.domain.model.TextData;
import com.muyu.goods.domain.resp.EngineConfigResp;
/**
* Service
*
*
* @author muyu
* @date 2024-05-04
*/
public interface IConfigService
public interface IConfigService
{
/**
*
*
*
* @param id
* @return
*/
@ -21,15 +23,22 @@ public interface IConfigService
/**
*
*
*
* @param config
* @return
*/
public List<Config> selectConfigList(Config config);
/**
*
* @param ruleId
* @return
*/
List<Config> listConfigEs(Long ruleId);
/**
*
*
*
* @param config
* @return
*/
@ -37,7 +46,7 @@ public interface IConfigService
/**
*
*
*
* @param config
* @return
*/
@ -45,7 +54,7 @@ public interface IConfigService
/**
*
*
*
* @param ids
* @return
*/
@ -53,9 +62,18 @@ public interface IConfigService
/**
*
*
*
* @param id
* @return
*/
public int deleteConfigById(Long id);
Object testData(TextData textData);
/**
*
* @param id
* @return
*/
EngineConfigResp getScopeInfo(Integer id);
}

View File

@ -1,12 +1,27 @@
package com.muyu.goods.service.impl;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import com.muyu.common.core.exception.ServiceException;
import com.muyu.goods.constant.ConfigCodeConstants;
import com.muyu.goods.constant.RuleOperationConstants;
import com.muyu.goods.domain.model.TextData;
import com.muyu.goods.domain.resp.EngineConfigResp;
import com.muyu.goods.dymamicLoad.DynamicLoader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.muyu.goods.mapper.ConfigMapper;
import com.muyu.goods.domain.Config;
import com.muyu.goods.service.IConfigService;
import javax.validation.constraints.Max;
/**
* Service
*
@ -43,6 +58,16 @@ public class ConfigServiceImpl implements IConfigService
return configMapper.selectConfigList(config);
}
/**
* y
* @param ruleId
* @return
*/
@Override
public List<Config> listConfigEs(Long ruleId) {
return configMapper.listConfigEs(ruleId);
}
/**
*
*
@ -90,4 +115,61 @@ public class ConfigServiceImpl implements IConfigService
{
return configMapper.deleteConfigById(id);
}
@Override
public Object testData(TextData textData) {
Object invoke = null;
try {
Config config = configMapper.selectConfigById(textData.getId());
String content = config.getRuleContent().replaceAll("\r\n", "");
// 对source进行编译生成class文件存放在Map中这里用bytecode接收
Map<String, byte[]> bytecode = DynamicLoader.compile(RuleOperationConstants.CLASS_NAME + RuleOperationConstants.FILE_SUFFIX,content );
// 加载class文件到虚拟机中然后通过反射执行
@SuppressWarnings("resource")
DynamicLoader.MemoryClassLoader classLoader = new DynamicLoader.MemoryClassLoader(
bytecode);
Class<?> clazz = classLoader.loadClass(RuleOperationConstants.CLASS_NAME);
// 调用ruleTest方法
Method mainMethod = clazz.getDeclaredMethod(RuleOperationConstants.METHOD_NAME, List.class);
invoke = mainMethod.invoke(null, textData.getList());
} catch (Exception e) {
e.printStackTrace();
throw new ServiceException("测试失败");
}
return invoke;
}
/**
*
* @param id
* @return
*/
@Override
public EngineConfigResp getScopeInfo(Integer id) {
String scope = ConfigCodeConstants.CONFIG_FILE_NAME_ARRAY[id];
String type = ConfigCodeConstants.CONFIG_FILE_TYPE_ARRAY[id];
String path = ConfigCodeConstants.BASE_FILE_PATH+scope;
String code = null;
try {
code = Files.readString(Paths.get(path));
} catch (IOException e) {
throw new RuntimeException(e);
}
return EngineConfigResp.builder()
.type(type)
.name(scope)
.code(code)
.build();
}
public static String ruleTest(List list) {
for (Object o : list) {
if (o.toString().contains("@163")){
list.remove(o);
}
}
return list.toString();
}
}

View File

@ -0,0 +1,36 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class stream {
public static void main(String[] args) throws IOException {
// stream 获取list
List<Integer> list = Arrays.asList(1,2,3);
System.out.println(list);
Stream<Integer> integerStream = list.stream();
List<Integer> collect1 = integerStream.collect(Collectors.toList());
System.out.println(collect1);
// stream 获取数组
Integer[] array = {1, 2, 3};
Stream<Integer> arrStream = Arrays.stream(array);
List<Integer> collect = arrStream.collect(Collectors.toList());
System.out.println(collect);
//中间处理
List<String> stringList = new ArrayList<>();
stringList.add("123456789@163.com");
stringList.add("2596325@163.com");
stringList.add("157492533@qq.com");
stringList.add("1547859633@qq.com");
//filter 筛选操作
// stringList.stream().filter(c -> c.contains("@163")).forEach(System.out::println);
//map 映射 map(表::展示字段)
stringList.stream().map(String::new).forEach(System.out::println);
//flatMap 将每个元素映射为一个Stream对象,并将所有的Stream对象的元素合并为一个steam对象
stringList.stream().flatMap(c -> Arrays.asList(c).stream()).forEach(System.out::println);
}
}

View File

@ -1,5 +1,15 @@
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class yes {
public static void main(String[] args) {
System.out.println("yes");
List<String> list = new ArrayList<>();
list.add("1424587994@163com");
list.add("2949451835@qq.com");
list.add("15975632@qq.com");
Stream<String> stream = list.stream();
stream.filter(c -> !c.contains("@163")).forEach(System.out::println);
}
}

View File

@ -8,11 +8,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="id" column="id" />
<result property="versionCode" column="version_code" />
<result property="ruleContent" column="rule_content" />
<result property="remark" column="remark" />
<result property="ruleId" column="rule_id" />
</resultMap>
<sql id="selectConfigVo">
select id, version_code, rule_content, rule_id from config
select id, version_code, rule_content, remark, rule_id from config
</sql>
<select id="selectConfigList" parameterType="com.muyu.goods.domain.Config" resultMap="ConfigResult">
@ -28,6 +29,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="selectConfigVo"/>
where id = #{id}
</select>
<select id="listConfigEs" resultType="com.muyu.goods.domain.Config">
<include refid="selectConfigVo"/>
where rule_id = #{ruleId}
</select>
<insert id="insertConfig" parameterType="com.muyu.goods.domain.Config" useGeneratedKeys="true" keyProperty="id">
insert into config