From 2690f0791d97878d5a6a30bc71bd33c934e30a30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?REBUILD=20=E4=BC=81=E4=B8=9A=E7=AE=A1=E7=90=86=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F?= <42044143+getrebuild@users.noreply.github.com> Date: Wed, 6 Dec 2023 11:46:58 +0800 Subject: [PATCH] Form calc date 109 (#689) * feat: eval-calc-formula --------- Co-authored-by: devezhao --- .../metadata/impl/EasyFieldConfigProps.java | 8 + .../service/trigger/aviator/AviatorUtils.java | 26 +++- .../trigger/aviator/OverOperatorType.java | 84 +++++++++++ .../web/general/ModelExtrasController.java | 90 ++++++++++++ .../web/admin/metadata/field-edit.html | 9 +- .../web/assets/js/general/rb-forms.js | 138 ++++++++---------- .../web/assets/js/metadata/field-edit.js | 10 +- .../trigger/aviator/AviatorUtilsTest.java | 39 +++-- 8 files changed, 303 insertions(+), 101 deletions(-) create mode 100644 src/main/java/com/rebuild/core/service/trigger/aviator/OverOperatorType.java diff --git a/src/main/java/com/rebuild/core/metadata/impl/EasyFieldConfigProps.java b/src/main/java/com/rebuild/core/metadata/impl/EasyFieldConfigProps.java index ffdb6b667..97010bb77 100644 --- a/src/main/java/com/rebuild/core/metadata/impl/EasyFieldConfigProps.java +++ b/src/main/java/com/rebuild/core/metadata/impl/EasyFieldConfigProps.java @@ -49,11 +49,19 @@ public class EasyFieldConfigProps { * 日期格式 */ public static final String DATE_FORMAT = "dateFormat"; + /** + * 表单公式 + */ + public static final String DATE_CALCFORMULA = NUMBER_CALCFORMULA; /** * 日期格式 */ public static final String DATETIME_FORMAT = "datetimeFormat"; + /** + * 表单公式 + */ + public static final String DATETIME_CALCFORMULA = NUMBER_CALCFORMULA; /** * 时间格式 diff --git a/src/main/java/com/rebuild/core/service/trigger/aviator/AviatorUtils.java b/src/main/java/com/rebuild/core/service/trigger/aviator/AviatorUtils.java index 43e8d4a9a..b1eda93e0 100644 --- a/src/main/java/com/rebuild/core/service/trigger/aviator/AviatorUtils.java +++ b/src/main/java/com/rebuild/core/service/trigger/aviator/AviatorUtils.java @@ -11,6 +11,7 @@ import com.googlecode.aviator.AviatorEvaluator; import com.googlecode.aviator.AviatorEvaluatorInstance; import com.googlecode.aviator.Options; import com.googlecode.aviator.exception.ExpressionSyntaxErrorException; +import com.googlecode.aviator.lexer.token.OperatorType; import com.googlecode.aviator.runtime.function.system.AssertFunction; import com.googlecode.aviator.runtime.type.AviatorFunction; import lombok.extern.slf4j.Slf4j; @@ -20,7 +21,7 @@ import java.util.Collections; import java.util.Map; /** - * // https://www.yuque.com/boyan-avfmj/aviatorscript + * https://www.yuque.com/boyan-avfmj/aviatorscript * * @author devezhao * @since 2021/4/12 @@ -43,6 +44,9 @@ public class AviatorUtils { } catch (Exception ignored) { } + AVIATOR.addOpFunction(OperatorType.ADD, new OverOperatorType.DateAdd()); + AVIATOR.addOpFunction(OperatorType.SUB, new OverOperatorType.DateSub()); + addCustomFunction(new DateDiffFunction()); addCustomFunction(new DateAddFunction()); addCustomFunction(new DateSubFunction()); @@ -55,13 +59,21 @@ public class AviatorUtils { } /** - * 表达式计算 - * * @param expression * @return + * @see #eval(String, Map, boolean) */ - public static Object evalQuietly(String expression) { - return eval(expression, null, true); + public static Object eval(String expression) { + return eval(expression, null, false); + } + + /** + * @param expression + * @return + * @see #eval(String, Map, boolean) + */ + public static Object eval(String expression, Map env) { + return eval(expression, env, false); } /** @@ -74,13 +86,13 @@ public class AviatorUtils { */ public static Object eval(String expression, Map env, boolean quietly) { try { - return AVIATOR.execute(expression, env); + return AVIATOR.execute(expression, env == null ? Collections.emptyMap() : env); } catch (Exception ex) { if (ex instanceof AssertFunction.AssertFailed) { throw new AssertFailedException((AssertFunction.AssertFailed) ex); } - log.error("Bad aviator expression : \n{}\n<< {}", expression, env, ex); + log.error("Bad aviator expression : \n>> {}\n>> {}\n>> {}", expression, env, ex.getLocalizedMessage()); if (!quietly) throw ex; } return null; diff --git a/src/main/java/com/rebuild/core/service/trigger/aviator/OverOperatorType.java b/src/main/java/com/rebuild/core/service/trigger/aviator/OverOperatorType.java new file mode 100644 index 000000000..6609dd9a8 --- /dev/null +++ b/src/main/java/com/rebuild/core/service/trigger/aviator/OverOperatorType.java @@ -0,0 +1,84 @@ +/*! +Copyright (c) REBUILD and/or its owners. All rights reserved. + +rebuild is dual-licensed under commercial and open source licenses (GPLv3). +See LICENSE and COMMERCIAL in the project root for license information. +*/ + +package com.rebuild.core.service.trigger.aviator; + +import cn.devezhao.commons.CalendarUtils; +import com.googlecode.aviator.lexer.token.OperatorType; +import com.googlecode.aviator.runtime.function.AbstractFunction; +import com.googlecode.aviator.runtime.type.AviatorLong; +import com.googlecode.aviator.runtime.type.AviatorObject; + +import java.util.Date; +import java.util.Map; + +/** + * 操作符重载 + * + * @author RB + * @since 2023/12/6 + */ +public class OverOperatorType { + + private OverOperatorType() {} + + /** + * 日期加 + */ + static class DateAdd extends AbstractFunction { + @Override + public String getName() { + return OperatorType.ADD.getToken(); + } + + @Override + public AviatorObject call(Map env, AviatorObject arg1, AviatorObject arg2) { + Object $argv1 = arg1.getValue(env); + Object $argv2 = arg2.getValue(env); + + if ($argv1 instanceof Date && $argv2 instanceof Number) { + return opDate((Date) $argv1, ((Number) $argv2).intValue()); + } else if ($argv2 instanceof Date && $argv1 instanceof Number) { + return opDate((Date) $argv2, ((Number) $argv1).intValue()); + } else { + return arg1.add(arg2, env); // Use default + } + } + } + + /** + * 日期减 + */ + static class DateSub extends AbstractFunction { + @Override + public String getName() { + return OperatorType.SUB.getToken(); + } + + @Override + public AviatorObject call(Map env, AviatorObject arg1, AviatorObject arg2) { + Object $argv1 = arg1.getValue(env); + Object $argv2 = arg2.getValue(env); + + if ($argv1 instanceof Date && $argv2 instanceof Number) { + return opDate((Date) $argv1, -((Number) $argv2).intValue()); + } else if ($argv2 instanceof Date && $argv1 instanceof Number) { + return opDate((Date) $argv2, -((Number) $argv1).intValue()); + } else if ($argv1 instanceof Date && $argv2 instanceof Date) { + int diff = CalendarUtils.getDayLeft((Date) $argv1, (Date) $argv2); + return AviatorLong.valueOf(diff); + } else { + return arg1.add(arg2, env); // Use default + } + } + } + + static AviatorDate opDate(Date date, int num) { + Date d = CalendarUtils.addDay(date, num); + return new AviatorDate(d); + } +} diff --git a/src/main/java/com/rebuild/web/general/ModelExtrasController.java b/src/main/java/com/rebuild/web/general/ModelExtrasController.java index bc970fd7d..c4ecb1649 100644 --- a/src/main/java/com/rebuild/web/general/ModelExtrasController.java +++ b/src/main/java/com/rebuild/web/general/ModelExtrasController.java @@ -8,22 +8,32 @@ See LICENSE and COMMERCIAL in the project root for license information. package com.rebuild.web.general; import cn.devezhao.bizz.privileges.impl.BizzPermission; +import cn.devezhao.commons.CalendarUtils; +import cn.devezhao.commons.ObjectUtils; +import cn.devezhao.commons.web.ServletUtils; import cn.devezhao.persist4j.Entity; import cn.devezhao.persist4j.Field; import cn.devezhao.persist4j.engine.ID; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONAware; +import com.alibaba.fastjson.JSONObject; import com.rebuild.api.RespBody; import com.rebuild.core.Application; import com.rebuild.core.configuration.general.AutoFillinManager; import com.rebuild.core.metadata.MetadataHelper; +import com.rebuild.core.metadata.easymeta.DisplayType; +import com.rebuild.core.metadata.easymeta.EasyDecimal; import com.rebuild.core.metadata.easymeta.EasyEntity; +import com.rebuild.core.metadata.easymeta.EasyField; import com.rebuild.core.metadata.easymeta.EasyMetaFactory; +import com.rebuild.core.metadata.impl.EasyFieldConfigProps; import com.rebuild.core.privileges.UserHelper; import com.rebuild.core.privileges.bizz.User; import com.rebuild.core.service.general.RepeatedRecordsException; import com.rebuild.core.service.general.transform.RecordTransfomer; +import com.rebuild.core.service.trigger.aviator.AviatorUtils; +import com.rebuild.core.support.general.ContentWithFieldVars; import com.rebuild.core.support.i18n.I18nUtils; import com.rebuild.core.support.i18n.Language; import com.rebuild.utils.JSONUtils; @@ -31,7 +41,9 @@ import com.rebuild.web.BaseController; import com.rebuild.web.EntityParam; import com.rebuild.web.IdParam; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -39,7 +51,9 @@ import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Set; /** * 表单/视图 功能扩展 @@ -195,4 +209,80 @@ public class ModelExtrasController extends BaseController { } return allowed; } + + @PostMapping("eval-calc-formula") + public RespBody evalCalcFormula(@EntityParam Entity entity, HttpServletRequest request) { + String targetField = getParameterNotNull(request, "field"); + if (!entity.containsField(targetField)) return RespBody.error(); + + JSONObject post = (JSONObject) ServletUtils.getRequestJson(request); + Map varsInFormula = post.getInnerMap(); + for (Object value : varsInFormula.values()) { + if (value == null || StringUtils.isBlank(value.toString())) { + return RespBody.ok(); + } + } + + EasyField easyField = EasyMetaFactory.valueOf(entity.getField(targetField)); + String formula = easyField.getExtraAttr(EasyFieldConfigProps.DATE_CALCFORMULA); + + boolean canCalc = true; + Set fieldVars = ContentWithFieldVars.matchsVars(formula); + for (String field : fieldVars) { + if (!entity.containsField(field)) { + canCalc = false; + break; + } + + Object fieldValue = varsInFormula.get(field); + if (fieldValue == null) { + canCalc = false; + break; + } + + String fieldValue2 = fieldValue.toString(); + DisplayType dt = EasyMetaFactory.valueOf(entity.getField(field)).getDisplayType(); + if (dt == DisplayType.DATE || dt == DisplayType.DATETIME) { + fieldValue = CalendarUtils.parse(fieldValue2, CalendarUtils.UTC_DATETIME_FORMAT.substring(0, fieldValue2.length())); + } else if (dt == DisplayType.NUMBER || dt == DisplayType.DECIMAL) { + fieldValue = EasyDecimal.clearFlaged(fieldValue2); + if (StringUtils.isNotBlank((String) fieldValue)) { + if (dt == DisplayType.NUMBER) fieldValue = ObjectUtils.toLong(fieldValue); + else fieldValue = ObjectUtils.toDouble(fieldValue); + } else { + fieldValue = null; + } + } + + if (fieldValue == null) { + canCalc = false; + break; + } + varsInFormula.put(field, fieldValue); + } + if (!canCalc) return RespBody.ok(); + + formula = formula + .replace("{", "").replace("}", "") + .replace("×", "*").replace("÷", "/"); + + Object evalVal = AviatorUtils.eval(formula, varsInFormula, true); + if (evalVal == null) return RespBody.ok(); + + DisplayType dt = easyField.getDisplayType(); + if (dt == DisplayType.DATE || dt == DisplayType.DATETIME) { + if (evalVal instanceof Date) { + evalVal = easyField.wrapValue(evalVal); + return RespBody.ok(evalVal); + } + } else if (dt == DisplayType.NUMBER || dt == DisplayType.DECIMAL) { + if (evalVal instanceof Number) { + evalVal = easyField.wrapValue(evalVal); + return RespBody.ok(evalVal); + } + } + + log.warn("Bad eval value `{}` for field : {}", evalVal, easyField.getRawMeta()); + return RespBody.ok(); + } } diff --git a/src/main/resources/web/admin/metadata/field-edit.html b/src/main/resources/web/admin/metadata/field-edit.html index 6c18135c0..b99c14773 100644 --- a/src/main/resources/web/admin/metadata/field-edit.html +++ b/src/main/resources/web/admin/metadata/field-edit.html @@ -83,7 +83,7 @@
[[${calcFormula ?: calcFormula}]]

diff --git a/src/main/resources/web/assets/js/general/rb-forms.js b/src/main/resources/web/assets/js/general/rb-forms.js index 6c5f033f9..1e0e99a6e 100644 --- a/src/main/resources/web/assets/js/general/rb-forms.js +++ b/src/main/resources/web/assets/js/general/rb-forms.js @@ -1165,80 +1165,8 @@ class RbFormNumber extends RbFormText { componentDidMount() { super.componentDidMount() - - // 表单计算(视图下无效) - if (this.props.calcFormula && !this.props.onView) { - const calcFormula = this.props.calcFormula.replace(new RegExp('×', 'ig'), '*').replace(new RegExp('÷', 'ig'), '/') - const fixed = this.props.decimalFormat ? (this.props.decimalFormat.split('.')[1] || '').length : 0 - - // 等待字段初始化完毕 - setTimeout(() => { - const calcFormulaValues = {} - const watchFields = calcFormula.match(/\{([a-z0-9]+)}/gi) || [] - - watchFields.forEach((item) => { - const name = item.substr(1, item.length - 2) - const fieldComp = this.props.$$$parent.refs[`fieldcomp-${name}`] - if (fieldComp && !$emptyNum(fieldComp.state.value)) { - calcFormulaValues[name] = this._removeComma(fieldComp.state.value) - } - }) - - // 表单计算 - let _timer - this.props.$$$parent.onFieldValueChange((s) => { - if (!watchFields.includes(`{${s.name}}`)) { - if (rb.env === 'dev') console.log('onFieldValueChange ignored :', s, this.props.field) - return false - } else if (rb.env === 'dev') { - console.log('onFieldValueChange for calcFormula :', s, this.props.field) - } - - // // fix: 3.2 字段相互使用导致死循环 - // this.__fixUpdateDepth = (this.__fixUpdateDepth || 0) + 1 - // if (this.__fixUpdateDepth > 9) { - // console.log(`Maximum update depth exceeded : ${this.props.field}=${this.props.calcFormula}`) - // setTimeout(() => (this.__fixUpdateDepth = 0), 100) - // return false - // } - - if ($emptyNum(s.value)) { - delete calcFormulaValues[s.name] - } else { - calcFormulaValues[s.name] = this._removeComma(s.value) - } - - let formula = calcFormula - for (let key in calcFormulaValues) { - formula = formula.replace(new RegExp(`{${key}}`, 'ig'), calcFormulaValues[key] || 0) - } - - if (_timer) { - clearTimeout(_timer) - _timer = null - } - - // v34 延迟执行 - _timer = setTimeout(() => { - // 还有变量无值 - if (formula.includes('{')) { - this.setValue(null) - return false - } - - try { - let calcv = null - eval(`calcv = ${formula}`) - if (!isNaN(calcv)) this.setValue(calcv.toFixed(fixed)) - } catch (err) { - if (rb.env === 'dev') console.log(err) - } - return true - }, 200) - // _timer end - }) - }, 200) // init - } + // 表单计算 + if (this.props.calcFormula && !this.props.onView) __calcFormula(this) } // 移除千分为位 @@ -1496,6 +1424,12 @@ class RbFormDateTime extends RbFormElement { wt = $(this._fieldValue).offset().top return wt + 280 < wh ? 'bottom-right' : 'top-right' } + + componentDidMount() { + super.componentDidMount() + // 表单计算 + if (this.props.calcFormula && !this.props.onView) __calcFormula(this) + } } class RbFormTime extends RbFormDateTime { @@ -3002,3 +2936,59 @@ const __addRecentlyUse = function (id) { $.post(`/commons/search/recently-add?id=${id}`) } } + +// 表单计算(视图下无效) +const __calcFormula = function (_this) { + const watchFields = _this.props.calcFormula.match(/\{([a-z0-9]+)}/gi) || [] + const $$$parent = _this.props.$$$parent + + const evalUrl = `/app/entity/extras/eval-calc-formula?entity=${$$$parent.props.entity}&field=${_this.props.field}` + setTimeout(() => { + const calcFormulaValues = {} + let _timer + + // init + watchFields.forEach((item) => { + const name = item.substr(1, item.length - 2) + const fieldComp = $$$parent.refs[`fieldcomp-${name}`] + if (fieldComp && !$empty(fieldComp.state.value)) { + calcFormulaValues[name] = fieldComp.state.value + } + }) + + // onchange + $$$parent.onFieldValueChange((s) => { + if (!watchFields.includes(`{${s.name}}`)) { + if (rb.env === 'dev') console.log('onFieldValueChange ignored :', s, _this.props.field) + return false + } else if (rb.env === 'dev') { + console.log('onFieldValueChange for calcFormula :', s, _this.props.field) + } + + if ($empty(s.value)) delete calcFormulaValues[s.name] + else calcFormulaValues[s.name] = s.value + + if (_timer) { + clearTimeout(_timer) + _timer = null + } + + // v36 + _timer = setTimeout(() => { + $.post(evalUrl, JSON.stringify(calcFormulaValues), (res) => { + if (res.data) _this.setValue(res.data) + else _this.setValue(null) + }) + }, 400) + return true + }) + + // 新建时 + if (_this._isNew) { + $.post(evalUrl, JSON.stringify(calcFormulaValues), (res) => { + if (res.data) _this.setValue(res.data) + else _this.setValue(null) + }) + } + }, 400) // delay for init +} diff --git a/src/main/resources/web/assets/js/metadata/field-edit.js b/src/main/resources/web/assets/js/metadata/field-edit.js index eea034390..799d9ebb8 100644 --- a/src/main/resources/web/assets/js/metadata/field-edit.js +++ b/src/main/resources/web/assets/js/metadata/field-edit.js @@ -232,6 +232,7 @@ $(document).ready(function () { } else if (dt === 'SERIES') { _handleSeries() } else if (dt === 'DATE' || dt === 'DATETIME' || dt === 'TIME') { + if (dt === 'DATE' || dt === 'DATETIME') _handleCalcFormula(extConfig.calcFormula) _handleDatetime(dt) } else if (dt === 'FILE' || dt === 'IMAGE') { _handleFile(extConfig.uploadNumber) @@ -247,8 +248,7 @@ $(document).ready(function () { } else if (dt === 'BARCODE') { $('.J_fieldAttrs input').attr('disabled', true) } else if (dt === 'NUMBER' || dt === 'DECIMAL') { - _handleNumber(extConfig.calcFormula) - + _handleCalcFormula(extConfig.calcFormula) if (dt === 'DECIMAL') { if (!extConfig.decimalType || extConfig.decimalType === 0 || extConfig.decimalType === '0' || extConfig.decimalType === '%') { // 数字、百分比 @@ -580,7 +580,7 @@ const _loadRefsLabel = function ($dv, $dvClear) { } let FIELDS_CACHE -const _handleNumber = function (calcFormula) { +const _handleCalcFormula = function (formula) { const $el = $('#calcFormula2') function _call(s) { $('#calcFormula').val(s || '') @@ -591,13 +591,13 @@ const _handleNumber = function (calcFormula) { $.get(`/commons/metadata/fields?entity=${wpc.entityName}`, (res) => { const fs = [] res.data.forEach((item) => { - if ((item.type === 'NUMBER' || item.type === 'DECIMAL') && item.name !== wpc.fieldName) { + if (['NUMBER', 'DECIMAL', 'DATE', 'DATETIME'].includes(item.type) && !['approvalLastTime', 'modifiedOn'].includes(item.name) && item.name !== wpc.fieldName) { fs.push(item) } }) FIELDS_CACHE = fs - if (calcFormula) _call(calcFormula) + if (formula) _call(formula) }) } diff --git a/src/test/java/com/rebuild/core/service/trigger/aviator/AviatorUtilsTest.java b/src/test/java/com/rebuild/core/service/trigger/aviator/AviatorUtilsTest.java index 3b1d06c7f..d7676131c 100644 --- a/src/test/java/com/rebuild/core/service/trigger/aviator/AviatorUtilsTest.java +++ b/src/test/java/com/rebuild/core/service/trigger/aviator/AviatorUtilsTest.java @@ -7,11 +7,13 @@ See LICENSE and COMMERCIAL in the project root for license information. package com.rebuild.core.service.trigger.aviator; +import cn.devezhao.commons.CalendarUtils; import com.googlecode.aviator.exception.ExpressionRuntimeException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Collections; +import java.util.HashMap; import java.util.Map; /** @@ -22,7 +24,7 @@ class AviatorUtilsTest { @Test void eval() { - System.out.println(AviatorUtils.evalQuietly("123*123")); + System.out.println(AviatorUtils.eval("123*123")); System.out.println(AviatorUtils.eval( "abc12_.abc+123", Collections.singletonMap("abc12_.abc", 100), true)); @@ -36,30 +38,30 @@ class AviatorUtilsTest { @Test void func() { - AviatorUtils.evalQuietly("p(DATEDIFF('2021-03-04 00:00:00', '2022-03-05 23:59:59', 'D'))"); - AviatorUtils.evalQuietly("p(DATEDIFF('2021-03-04 00:00:00', '2022-03-05 23:59:59', 'M'))"); - AviatorUtils.evalQuietly("p(DATEDIFF('2021-03-04 00:00:00', '2022-03-09 23:59:59', 'Y'))"); + AviatorUtils.eval("p(DATEDIFF('2021-03-04 00:00:00', '2022-03-05 23:59:59', 'D'))"); + AviatorUtils.eval("p(DATEDIFF('2021-03-04 00:00:00', '2022-03-05 23:59:59', 'M'))"); + AviatorUtils.eval("p(DATEDIFF('2021-03-04 00:00:00', '2022-03-09 23:59:59', 'Y'))"); - AviatorUtils.evalQuietly("p(DATEADD('2021-01-01 18:17:00', '2H'))"); + AviatorUtils.eval("p(DATEADD('2021-01-01 18:17:00', '2H'))"); - AviatorUtils.evalQuietly("p(DATESUB('2021-01-01 18:17:00', '1'))"); + AviatorUtils.eval("p(DATESUB('2021-01-01 18:17:00', '1'))"); } @Test void funcComplex() { - AviatorUtils.evalQuietly("p(100 + DATEDIFF('2021-01-01 18:17:00', '2021-01-01 16:17:00', 'H'))"); - AviatorUtils.evalQuietly("p(DATEADD(DATEADD('2021-01-01 18:17:00', '2H'), '1D'))"); + AviatorUtils.eval("p(100 + DATEDIFF('2021-01-01 18:17:00', '2021-01-01 16:17:00', 'H'))"); + AviatorUtils.eval("p(DATEADD(DATEADD('2021-01-01 18:17:00', '2H'), '1D'))"); } @Test void funcRequestFunction() { - AviatorUtils.evalQuietly("p(REQUEST('https://www.baidu.com/'))"); - AviatorUtils.evalQuietly("p(REQUEST('https://www.google.com/', 'imdefault'))"); + AviatorUtils.eval("p(REQUEST('https://www.baidu.com/'))"); + AviatorUtils.eval("p(REQUEST('https://www.google.com/', 'imdefault'))"); } @Test void funcLocationDistanceFunction() { - AviatorUtils.evalQuietly("p(LOCATIONDISTANCE('123.456789,123.456789', '地址$$$$123.456789,123.456789'))"); + AviatorUtils.eval("p(LOCATIONDISTANCE('123.456789,123.456789', '地址$$$$123.456789,123.456789'))"); } @Test @@ -75,6 +77,19 @@ class AviatorUtilsTest { @Test void testJava() { - AviatorUtils.evalQuietly("p(StringUtils.upperCase('abcd'));"); + AviatorUtils.eval("p(StringUtils.upperCase('abcd'));"); + } + + @Test + void testDateOp() { + Map env = new HashMap<>(); + env.put("date1", CalendarUtils.now()); + AviatorUtils.eval("p(date1 + 8)", env, true); + AviatorUtils.eval("p(date1 - 8)", env, true); + AviatorUtils.eval("p(date1 - date1)", env, true); + + // BAD + Assertions.assertThrows(ExpressionRuntimeException.class, + () -> AviatorUtils.eval("date1 + date1", env, false)); } } \ No newline at end of file