添加公式

This commit is contained in:
2024-06-03 22:04:27 +08:00
parent 30c190b94d
commit d8dd80a274
7 changed files with 274 additions and 64 deletions

View File

@@ -2,17 +2,26 @@ package cn.lihongjie.coal.common;
import cn.lihongjie.coal.exception.BizException; import cn.lihongjie.coal.exception.BizException;
import groovy.lang.Binding; import com.google.common.cache.Cache;
import groovy.lang.GroovyShell; import com.google.common.cache.CacheBuilder;
import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.Script;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.GroovyCodeVisitorAdapter; import org.codehaus.groovy.ast.CodeVisitorSupport;
import org.codehaus.groovy.ast.builder.AstStringCompiler; import org.codehaus.groovy.ast.builder.AstStringCompiler;
import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.springframework.util.StopWatch;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -26,6 +35,12 @@ import java.util.stream.Collectors;
@Slf4j @Slf4j
public class GroovyScriptUtils { public class GroovyScriptUtils {
private static final Cache<String, Class<?>> scriptCache =
CacheBuilder.newBuilder().maximumSize(10000).build();
private static final Cache<Class<?>, Script> scriptInstanceCache =
CacheBuilder.newBuilder().maximumSize(10000).build();
public String replaceVariable( public String replaceVariable(
String patternstr, String script, Function<String, String> mapper) { String patternstr, String script, Function<String, String> mapper) {
@@ -60,6 +75,12 @@ public class GroovyScriptUtils {
throw new BizException("无效的计算公式: " + e.getMessage()); throw new BizException("无效的计算公式: " + e.getMessage());
} }
} }
private static final GroovyClassLoader groovyClassLoader;
static {
CompilerConfiguration config = new CompilerConfiguration();
groovyClassLoader = new GroovyClassLoader(GroovyScriptUtils.class.getClassLoader(), config);
}
public static List<String> variables(String formula) { public static List<String> variables(String formula) {
if (StringUtils.isEmpty(formula)) { if (StringUtils.isEmpty(formula)) {
@@ -73,7 +94,7 @@ public class GroovyScriptUtils {
x -> { x -> {
ArrayList<String> ans = new ArrayList<>(); ArrayList<String> ans = new ArrayList<>();
x.visit( x.visit(
new GroovyCodeVisitorAdapter() { new CodeVisitorSupport() {
@Override @Override
public void visitVariableExpression( public void visitVariableExpression(
VariableExpression expression) { VariableExpression expression) {
@@ -83,13 +104,62 @@ public class GroovyScriptUtils {
return ans.stream(); return ans.stream();
}) })
.distinct()
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@SneakyThrows
public static Object exec(String formula0, Map<String, Object> map) { public static Object exec(String formula0, Map<String, Object> map) {
return exec(formula0, map, true, false);
}
GroovyShell shell = new GroovyShell(new Binding(map)); @SneakyThrows
public static Object exec(String formula0, Map<String, Object> map, boolean reuseScriptInstance, boolean logTime) {
return shell.evaluate(formula0); if (StringUtils.isEmpty(formula0)) {
return null;
}
if (MapUtils.isEmpty(map)) {
map = Map.of();
}
String key = DigestUtils.md5Hex(formula0);
StopWatch stopWatch = null;
if (logTime) {
stopWatch = new StopWatch("exec " + key);
}
if (stopWatch != null) stopWatch.start("parseClass");
Class parsedClass = scriptCache.get(key, () -> groovyClassLoader.parseClass(formula0));
if (stopWatch != null) stopWatch.stop();
if (stopWatch != null) stopWatch.start("newInstance");
Script script = reuseScriptInstance ? scriptInstanceCache.get(parsedClass, () -> (Script) parsedClass.newInstance()): (Script) parsedClass.newInstance();
if (stopWatch != null) stopWatch.stop();
if (stopWatch != null) stopWatch.start("binding");
script.setBinding(new Binding(map));
if (stopWatch != null) stopWatch.stop();
if (stopWatch != null) stopWatch.start("run");
Object run = script.run();
if (stopWatch != null) stopWatch.stop();
if (stopWatch != null) {
log.info(stopWatch.prettyPrint());
}
return run;
} }
} }

View File

@@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@@ -106,9 +107,8 @@ public class ReflectUtils {
return fieldCache.get(cls, () -> FieldUtils.getAllFieldsList(cls)); return fieldCache.get(cls, () -> FieldUtils.getAllFieldsList(cls));
} }
@SneakyThrows @SneakyThrows
public static List<Field> getAllFieldsList(Class cls){ public static List<Field> getAllFieldsList(Class cls) {
return doGetAllFields(cls); return doGetAllFields(cls);
} }
@@ -135,14 +135,11 @@ public class ReflectUtils {
return null; return null;
} }
return getField(o.getClass(), field) return getField(o.getClass(), field).map(x -> readField(x, o, forceAccess)).orElse(null);
.map(x -> getReadField(x, o, forceAccess))
.orElse(null);
} }
@SneakyThrows @SneakyThrows
public static Object getReadField(Field x, Object o, boolean forceAccess){ public static Object readField(Field x, Object o, boolean forceAccess) {
return FieldUtils.readField(x, o, forceAccess); return FieldUtils.readField(x, o, forceAccess);
} }
@@ -157,12 +154,13 @@ public class ReflectUtils {
return; return;
} }
getField(o.getClass(), field) getField(o.getClass(), field).ifPresent(x -> writeField(x, o, val, forceAccess));
.ifPresent(x -> writeField(x, o, val, forceAccess));
} }
@SneakyThrows @SneakyThrows
public static void writeField(final Field field, final Object target, final Object value, final boolean forceAccess){ public static void writeField(
FieldUtils.writeField(field, target, value, forceAccess); final Field field, final Object target, final Object value, final boolean forceAccess) {
FieldUtils.writeField(field, target, value, forceAccess);
} }
public static Object getFieldValue(Object object, String fieldName) { public static Object getFieldValue(Object object, String fieldName) {
@@ -185,13 +183,24 @@ public class ReflectUtils {
public static List<String> idList(Object dtos) { public static List<String> idList(Object dtos) {
if (dtos instanceof Iterable<?>) { if (dtos instanceof Iterable<?>) {
return ((List<?>) dtos).stream().map(ReflectUtils::getId).toList(); return ((List<?>) dtos).stream().map(ReflectUtils::getId).toList();
}else { } else {
return List.of(ReflectUtils.getId(dtos)); return List.of(ReflectUtils.getId(dtos));
} }
} }
public static Map<String, Object> toMap(Object entity) {
if (entity == null) {
return Map.of();
}
Map<String, Object> map = new HashMap<>();
for (Field e : getAllFieldsList(entity.getClass())) {
map.put(e.getName(), readField(e, entity, true));
}
return map;
}
} }

View File

@@ -4,8 +4,15 @@ import cn.lihongjie.coal.base.dto.OrgCommonDto;
import lombok.Data; import lombok.Data;
import org.hibernate.annotations.Comment;
@Data @Data
public class WeightColumnConfigDto extends OrgCommonDto { public class WeightColumnConfigDto extends OrgCommonDto {
private String displayName; private String displayName;
private Integer width; private Integer width;
@Comment("groovy脚本")
private String script;
} }

View File

@@ -18,9 +18,13 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
@@ -68,6 +72,8 @@ public class WeightColumnConfigService
return getById(entity.getId()); return getById(entity.getId());
} }
@PersistenceContext EntityManager em;
private void validateScript(UpdateWeightColumnConfigDto request) { private void validateScript(UpdateWeightColumnConfigDto request) {
if (StringUtils.isBlank(request.getScript())) { if (StringUtils.isBlank(request.getScript())) {
@@ -77,10 +83,31 @@ public class WeightColumnConfigService
String script = request.getScript(); String script = request.getScript();
GroovyScriptUtils.validate(script); GroovyScriptUtils.validate(script);
List<String> variables = GroovyScriptUtils.variables(request.getScript());
if (CollectionUtils.isNotEmpty(variables)) {
for (String variable : variables) {
Long cnt = (Long) em.createQuery("select count(*) from WeightColumnConfigEntity where code = :variable and status = 1 and organizationId = :organizationId")
.setParameter("variable", variable)
.setParameter("organizationId", Ctx.currentUser().getOrganizationId())
.getSingleResult();
if ( cnt == null || cnt == 0) {
throw new RuntimeException("变量不存在或者被禁用: " + variable);
}
}
}
} }
public void runScriptOnOldData(IdRequest request){ public void runScriptOnOldData(IdRequest request) {
WeightColumnConfigEntity entity = this.repository.get(request.getIds().get(0)); WeightColumnConfigEntity entity = this.repository.get(request.getIds().get(0));
@@ -89,19 +116,8 @@ public class WeightColumnConfigService
} }
String script = entity.getScript(); String script = entity.getScript();
} }
public void delete(IdRequest request) { public void delete(IdRequest request) {
this.repository.deleteAllById(request.getIds()); this.repository.deleteAllById(request.getIds());
} }

View File

@@ -51,6 +51,10 @@ hour
private List<String> mzUserList; private List<String> mzUserList;
private String pzUser; private String pzUser;
private List<String> pzUserList; private List<String> pzUserList;
private List<String> weighTypeList;
@Data @Data
public static class FieldInfo { public static class FieldInfo {

View File

@@ -4,12 +4,15 @@ import cn.lihongjie.coal.base.dto.CommonQuery;
import cn.lihongjie.coal.base.dto.IdRequest; import cn.lihongjie.coal.base.dto.IdRequest;
import cn.lihongjie.coal.base.service.BaseService; import cn.lihongjie.coal.base.service.BaseService;
import cn.lihongjie.coal.common.Ctx; import cn.lihongjie.coal.common.Ctx;
import cn.lihongjie.coal.common.GroovyScriptUtils;
import cn.lihongjie.coal.common.JpaUtils; import cn.lihongjie.coal.common.JpaUtils;
import cn.lihongjie.coal.common.ReflectUtils;
import cn.lihongjie.coal.dataCollector.service.DataCollectorService; import cn.lihongjie.coal.dataCollector.service.DataCollectorService;
import cn.lihongjie.coal.dataCollectorLog.service.DataCollectorLogService; import cn.lihongjie.coal.dataCollectorLog.service.DataCollectorLogService;
import cn.lihongjie.coal.dbFunctions.DbFunctionService; import cn.lihongjie.coal.dbFunctions.DbFunctionService;
import cn.lihongjie.coal.exception.BizException; import cn.lihongjie.coal.exception.BizException;
import cn.lihongjie.coal.rabbitmq.RabbitMQService; import cn.lihongjie.coal.rabbitmq.RabbitMQService;
import cn.lihongjie.coal.weightColumnConfig.entity.WeightColumnConfigEntity;
import cn.lihongjie.coal.weightDevice.entity.WeightDeviceEntity; import cn.lihongjie.coal.weightDevice.entity.WeightDeviceEntity;
import cn.lihongjie.coal.weightDevice.mapper.WeightDeviceMapper; import cn.lihongjie.coal.weightDevice.mapper.WeightDeviceMapper;
import cn.lihongjie.coal.weightDevice.service.WeightDeviceService; import cn.lihongjie.coal.weightDevice.service.WeightDeviceService;
@@ -49,6 +52,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -343,6 +347,46 @@ where 更新时间>='%s' and 更新时间<='%s'
this.repository.unArchive(dto); this.repository.unArchive(dto);
} }
@Override
public WeightDeviceDataEntity save(WeightDeviceDataEntity entity) {
List<WeightColumnConfigEntity> configs =
em.createQuery(
"select w from WeightColumnConfigEntity w where w.organizationId = :organizationId and w.script is not null and length(w.script) > 0 and w.status = 1 order by w.sortKey asc ",
WeightColumnConfigEntity.class)
.setParameter("organizationId", entity.getOrganizationId())
.getResultList();
if (!CollectionUtils.isEmpty(configs)) {
for (WeightColumnConfigEntity config : configs) {
if (StringUtils.isNotBlank(config.getScript())) {
Object result = null;
try {
result =
GroovyScriptUtils.exec(
config.getScript(), ReflectUtils.toMap(entity));
} catch (Exception e) {
log.error("exec script error {}", config.getScript(), e);
}
try {
ReflectUtils.writeField(entity, config.getCode(), result);
} catch (Exception e) {
log.error("write field error {}", config.getCode(), e);
}
}
}
}
return super.save(entity);
}
public Page report(WeightDeviceDataReportRequest request) { public Page report(WeightDeviceDataReportRequest request) {
if (StringUtils.isEmpty(request.getTimeDimension())) { if (StringUtils.isEmpty(request.getTimeDimension())) {
@@ -429,11 +473,6 @@ where 更新时间>='%s' and 更新时间<='%s'
where += " and d.specification = :specification "; where += " and d.specification = :specification ";
} }
if (CollectionUtils.isNotEmpty(request.getPlateNoList())) { if (CollectionUtils.isNotEmpty(request.getPlateNoList())) {
where += " and d.plate_no in :plateNoList "; where += " and d.plate_no in :plateNoList ";
} }
@@ -454,7 +493,6 @@ where 更新时间>='%s' and 更新时间<='%s'
where += " and d.specification in :specificationList "; where += " and d.specification in :specificationList ";
} }
if (CollectionUtils.isNotEmpty(request.getMzUserList())) { if (CollectionUtils.isNotEmpty(request.getMzUserList())) {
where += " and d.mz_user in :mzUserList "; where += " and d.mz_user in :mzUserList ";
} }
@@ -463,8 +501,9 @@ where 更新时间>='%s' and 更新时间<='%s'
where += " and d.pz_user in :pzUserList "; where += " and d.pz_user in :pzUserList ";
} }
if (CollectionUtils.isNotEmpty(request.getWeighTypeList())) {
where += " and d.weigh_type in :weighTypeList ";
}
where += " and ( d.invalid is null or not d.invalid ) "; where += " and ( d.invalid is null or not d.invalid ) ";
where += " and ( d.finished is null or d.finished ) "; where += " and ( d.finished is null or d.finished ) ";
@@ -540,7 +579,6 @@ where 更新时间>='%s' and 更新时间<='%s'
countQuery.setParameter("specification", "%" + request.getSpecification() + "%"); countQuery.setParameter("specification", "%" + request.getSpecification() + "%");
} }
if (CollectionUtils.isNotEmpty(request.getPlateNoList())) { if (CollectionUtils.isNotEmpty(request.getPlateNoList())) {
selectQuery.setParameter("plateNoList", request.getPlateNoList()); selectQuery.setParameter("plateNoList", request.getPlateNoList());
countQuery.setParameter("plateNoList", request.getPlateNoList()); countQuery.setParameter("plateNoList", request.getPlateNoList());
@@ -551,10 +589,11 @@ where 更新时间>='%s' and 更新时间<='%s'
countQuery.setParameter("sendOrganizationList", request.getSendOrganizationList()); countQuery.setParameter("sendOrganizationList", request.getSendOrganizationList());
} }
if (CollectionUtils.isNotEmpty(request.getReceiveOrganizationList())) { if (CollectionUtils.isNotEmpty(request.getReceiveOrganizationList())) {
selectQuery.setParameter("receiveOrganizationList", request.getReceiveOrganizationList()); selectQuery.setParameter(
countQuery.setParameter("receiveOrganizationList", request.getReceiveOrganizationList()); "receiveOrganizationList", request.getReceiveOrganizationList());
countQuery.setParameter(
"receiveOrganizationList", request.getReceiveOrganizationList());
} }
if (CollectionUtils.isNotEmpty(request.getGoodsList())) { if (CollectionUtils.isNotEmpty(request.getGoodsList())) {
@@ -562,29 +601,26 @@ where 更新时间>='%s' and 更新时间<='%s'
countQuery.setParameter("goodsList", request.getGoodsList()); countQuery.setParameter("goodsList", request.getGoodsList());
} }
if (CollectionUtils.isNotEmpty(request.getSpecificationList())) { if (CollectionUtils.isNotEmpty(request.getSpecificationList())) {
selectQuery.setParameter("specificationList", request.getSpecificationList()); selectQuery.setParameter("specificationList", request.getSpecificationList());
countQuery.setParameter("specificationList", request.getSpecificationList()); countQuery.setParameter("specificationList", request.getSpecificationList());
} }
if (CollectionUtils.isNotEmpty(request.getMzUserList())) {
if (CollectionUtils.isNotEmpty(request.getMzUserList())){
selectQuery.setParameter("mzUserList", request.getMzUserList()); selectQuery.setParameter("mzUserList", request.getMzUserList());
countQuery.setParameter("mzUserList", request.getMzUserList()); countQuery.setParameter("mzUserList", request.getMzUserList());
} }
if (CollectionUtils.isNotEmpty(request.getPzUserList())){ if (CollectionUtils.isNotEmpty(request.getPzUserList())) {
selectQuery.setParameter("pzUserList", request.getPzUserList()); selectQuery.setParameter("pzUserList", request.getPzUserList());
countQuery.setParameter("pzUserList", request.getPzUserList()); countQuery.setParameter("pzUserList", request.getPzUserList());
} }
if (CollectionUtils.isNotEmpty(request.getWeighTypeList())) {
selectQuery.setParameter("weighTypeList", request.getWeighTypeList());
countQuery.setParameter("weighTypeList", request.getWeighTypeList());
}
var resultList = JpaUtils.convertTuplesToMap(selectQuery.getResultList()); var resultList = JpaUtils.convertTuplesToMap(selectQuery.getResultList());
@@ -604,7 +640,12 @@ where 更新时间>='%s' and 更新时间<='%s'
.LOWER_CAMEL, .LOWER_CAMEL,
e.getKey() e.getKey()
.toString()), .toString()),
e -> ObjectUtils.defaultIfNull(e.getValue(), "")))) e ->
ObjectUtils
.defaultIfNull(
e
.getValue(),
""))))
.toList(); .toList();
return new PageImpl<>( return new PageImpl<>(
@@ -618,30 +659,37 @@ where 更新时间>='%s' and 更新时间<='%s'
int cnt = int cnt =
em.createQuery( em.createQuery(
"update WeightDeviceDataEntity d set d.invalid = true where d.sequenceNumber not in :ids and d.dataUpdateTime >= :start and d.dataUpdateTime <= :end and d.device.id = :id and d.archiveStatus = '0'") "update WeightDeviceDataEntity d set d.invalid = true where d.sequenceNumber not in :ids and d.dataUpdateTime >= :start and d.dataUpdateTime <= :end and d.device.id = :id and d.archiveStatus = '0'")
.setParameter("ids", seqs) .setParameter("ids", Arrays.stream(seqs).mapToObj(x -> x).toList())
.setParameter("start", start) .setParameter("start", start)
.setParameter("end", end) .setParameter("end", end)
.setParameter("id", id) .setParameter("id", id)
.executeUpdate(); .executeUpdate();
log.info("invalidate {} records, start {} end {} deviceId {} seqsLen {} ", cnt, start, end, id, seqs.length); log.info(
"invalidate {} records, start {} end {} deviceId {} seqsLen {} ",
cnt,
start,
end,
id,
seqs.length);
int cnt2 = int cnt2 =
em.createQuery( em.createQuery(
"update WeightDeviceDataEntity d set d.invalid = false where d.sequenceNumber in :ids and d.dataUpdateTime >= :start and d.dataUpdateTime <= :end and d.device.id = :id and d.archiveStatus = '0'") "update WeightDeviceDataEntity d set d.invalid = false where d.sequenceNumber in :ids and d.dataUpdateTime >= :start and d.dataUpdateTime <= :end and d.device.id = :id and d.archiveStatus = '0'")
.setParameter("ids", seqs) .setParameter("ids", Arrays.stream(seqs).mapToObj(x -> x).toList())
.setParameter("start", start) .setParameter("start", start)
.setParameter("end", end) .setParameter("end", end)
.setParameter("id", id) .setParameter("id", id)
.executeUpdate(); .executeUpdate();
log.info("validate {} records, start {} end {} deviceId {} seqsLen {} ", cnt2, start, end, id, seqs.length); log.info(
"validate {} records, start {} end {} deviceId {} seqsLen {} ",
cnt2,
start,
end,
id,
seqs.length);
return cnt; return cnt;
} }
} }

View File

@@ -17,4 +17,60 @@ class GroovyScriptUtilsTest {
assertEquals( assertEquals(
"1 + 1", GroovyScriptUtils.replaceVariable("\\【(.+?)\\", "1 + 1", (s) -> "1")); "1 + 1", GroovyScriptUtils.replaceVariable("\\【(.+?)\\", "1 + 1", (s) -> "1"));
} }
@Test
void testExec() {
assertEquals(1, GroovyScriptUtils.exec("1", new HashMap<>()));
assertEquals(2, GroovyScriptUtils.exec("1 + 1", new HashMap<>()));
assertEquals(2, GroovyScriptUtils.exec("a + b", Map.of("a", 1, "b", 1)));
assertEquals(
2,
GroovyScriptUtils.exec(
"(a + (b?.isDouble() ? b.toDouble() : 0)) as Integer",
Map.of("a", 1, "b", "1")));
assertEquals(
1,
GroovyScriptUtils.exec(
"(a + (b?.isDouble() ? b.toDouble() : 0)) as Integer",
Map.of("a", 1, "b", "")));
}
@Test
void testExec1000() {
for (int i = 0; i < 10000; i++) {
assertEquals(2, GroovyScriptUtils.exec("a + b", Map.of("a", 1, "b", 1), false,false));
}
}
@Test
void testExec1000_reuse() {
for (int i = 0; i < 10000; i++) {
assertEquals(2, GroovyScriptUtils.exec("a + b", Map.of("a", 1, "b", 1), true,false));
}
}
@Test
void testVariables() {
assertEquals(
List.of("a", "b"),
GroovyScriptUtils.variables("a + b"));
assertEquals(
List.of("a", "b"),
GroovyScriptUtils.variables("a + b + a"));
assertEquals(
List.of("a", "b"),
GroovyScriptUtils.variables("a + b + a + b"));
assertEquals(
List.of("a", "b", "c"),
GroovyScriptUtils.variables("a + b + a + b + c"));
}
} }