From bb8a20656812323cebae45ad51abf14e81509a03 Mon Sep 17 00:00:00 2001 From: devezhao Date: Thu, 29 Aug 2019 17:27:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=B6=E9=97=B4=E6=97=A5?= =?UTF-8?q?=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/service/query/AdvFilterParser.java | 142 ++++++++++-------- src/main/webapp/assets/js/rb-advfilter.jsx | 4 +- .../service/query/AdvFilterParserTest.java | 30 ++++ 3 files changed, 109 insertions(+), 67 deletions(-) diff --git a/src/main/java/com/rebuild/server/service/query/AdvFilterParser.java b/src/main/java/com/rebuild/server/service/query/AdvFilterParser.java index 8d09d1da1..bc8d492ca 100644 --- a/src/main/java/com/rebuild/server/service/query/AdvFilterParser.java +++ b/src/main/java/com/rebuild/server/service/query/AdvFilterParser.java @@ -33,7 +33,6 @@ import com.rebuild.server.metadata.entity.DisplayType; import com.rebuild.server.metadata.entity.EasyMeta; import com.rebuild.server.service.bizz.UserHelper; import com.rebuild.server.service.bizz.privileges.Department; -import com.rebuild.web.IllegalParameterException; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; @@ -169,44 +168,55 @@ public class AdvFilterParser { if (hasAndFlag) { field = field.substring(1); } - - final String[] fieldPath = field.split("\\."); - if (fieldPath.length > 2) { - throw new IllegalParameterException("Unsupportted joins : " + field); - } - - if (!rootEntity.containsField(fieldPath[0])) { + + final Field fieldMeta = MetadataHelper.getLastJoinField(rootEntity, field); + if (fieldMeta == null) { LOG.warn("Unknow field '" + field + "' in '" + rootEntity.getName() + "'"); return null; } - - Field fieldMeta = rootEntity.getField(fieldPath[0]); - if (fieldPath.length > 1) { - if (EasyMeta.getDisplayType(fieldMeta) != DisplayType.REFERENCE) { - throw new IllegalParameterException("Non reference-field : " + field); - } - fieldMeta = fieldMeta.getReferenceEntity().getField(fieldPath[1]); - } - - DisplayType dt = EasyMeta.getDisplayType(fieldMeta); - // TODO 分类字段仅能查询最后一级 + + final DisplayType dt = EasyMeta.getDisplayType(fieldMeta); if (dt == DisplayType.CLASSIFICATION || hasAndFlag) { field = "&" + field; } - - final String op = item.getString("op"); - + + String op = item.getString("op"); + String value = item.getString("value"); + String valueEnd = null; + + // 日期时间 + if (dt == DisplayType.DATETIME || dt == DisplayType.DATE) { + if ("TDA".equalsIgnoreCase(op) || "YTA".equalsIgnoreCase(op) || "TTA".equalsIgnoreCase(op)) { + value = getUTCDateFormat().format(CalendarUtils.now()); + if ("YTA".equalsIgnoreCase(op)) { + value = getUTCDateFormat().format(CalendarUtils.addDay(-1)); + } else if ("TTA".equalsIgnoreCase(op)) { + value = getUTCDateFormat().format(CalendarUtils.addDay(1)); + } + + if (dt == DisplayType.DATETIME) { + op = "BW"; + valueEnd = parseValue(value, op, fieldMeta, true); + } else { + op = "EQ"; + } + } + + if ("EQ".equalsIgnoreCase(op) && dt == DisplayType.DATETIME && StringUtils.length(value) == 10) { + op = "BW"; + valueEnd = parseValue(value, op, fieldMeta, true); + } + } + StringBuilder sb = new StringBuilder(field) .append(' ') - .append(convOp(op)) - .append(' '); + .append(convOp(op)); if (op.equalsIgnoreCase("NL") || op.equalsIgnoreCase("NT")) { return sb.toString(); + } else { + sb.append(' '); } - - String value = item.getString("value"); - String valueEnd = null; - + // TODO 自定义函数 if ("BFD".equalsIgnoreCase(op)) { @@ -221,19 +231,7 @@ public class AdvFilterParser { value = getUTCDateFormat().format(CalendarUtils.addDay(-NumberUtils.toInt(value))) + FULL_TIME; } else if ("REM".equalsIgnoreCase(op)) { value = getUTCDateFormat().format(CalendarUtils.addMonth(-NumberUtils.toInt(value))) + FULL_TIME; - } else if ("YTA".equalsIgnoreCase(op)) { - value = getUTCDateFormat().format(CalendarUtils.addDay(-1)); - valueEnd = value + FULL_TIME; - value = value + ZERO_TIME; - } else if ("TDA".equalsIgnoreCase(op)) { - value = getUTCDateFormat().format(CalendarUtils.now()); - valueEnd = value + FULL_TIME; - value = value + ZERO_TIME; - } else if ("TTA".equalsIgnoreCase(op)) { - value = getUTCDateFormat().format(CalendarUtils.addDay(1)); - valueEnd = value + FULL_TIME; - value = value + ZERO_TIME; - } else if ("SFU".equalsIgnoreCase(op)) { + } else if ("SFU".equalsIgnoreCase(op)) { value = Application.getCurrentUser().toLiteral(); } else if ("SFB".equalsIgnoreCase(op)) { Department dept = UserHelper.getDepartment(Application.getCurrentUser()); @@ -257,40 +255,45 @@ public class AdvFilterParser { } } } - + if (StringUtils.isBlank(value)) { LOG.warn("Invalid item of AdvFilter : " + item.toJSONString()); return null; } - - // 占位 {1} + + // 快速搜索的占位符 {1} if (value.matches("\\{\\d+\\}")) { if (values == null) { LOG.warn("Invalid item of AdvFilter : " + item.toJSONString()); return null; } - + String valHold = value.replaceAll("[\\{\\}]", ""); - value = parseValue(values.get(valHold), op, fieldMeta); + value = parseValue(values.get(valHold), op, fieldMeta, false); } else { - value = parseValue(value, op, fieldMeta); + value = parseValue(value, op, fieldMeta, false); } // No value if (value == null) { + LOG.warn("Invalid item of AdvFilter : " + item.toJSONString()); return null; } // 区间 boolean isBetween = op.equalsIgnoreCase("BW"); - valueEnd = isBetween ? parseValue(item.getString("value2"), op, fieldMeta) : valueEnd; if (isBetween && valueEnd == null) { - valueEnd = value; - } - + valueEnd = parseValue(item.getString("value2"), op, fieldMeta, true); + if (valueEnd == null) { + valueEnd = value; + } + } + + // IN if (op.equalsIgnoreCase("IN") || op.equalsIgnoreCase("NIN") || op.equalsIgnoreCase("SFD")) { sb.append(value); } else { + // LIKE if (op.equalsIgnoreCase("LK") || op.equalsIgnoreCase("NLK")) { value = '%' + value + '%'; } @@ -298,25 +301,23 @@ public class AdvFilterParser { } if (isBetween) { - sb.append(" and ").append(quoteValue(valueEnd, fieldMeta.getType())); - } else if (valueEnd != null) { - sb.insert(0, '(') - .append(" and ") - .append(field) - .append(" <= ") - .append(quoteValue(valueEnd, fieldMeta.getType())) - .append(')'); - } - return sb.toString(); + sb.insert(0, "( ") + .append(" and ").append(quoteValue(valueEnd, fieldMeta.getType())) + .append(" )"); + } + + System.out.println(item + "\n>> " + sb); + return sb.toString().trim(); } - + /** * @param val * @param op * @param field + * @param endVal 仅对日期时间有意义 * @return */ - private String parseValue(Object val, String op, Field field) { + private String parseValue(Object val, String op, Field field, boolean endVal) { String value; // IN if (val instanceof JSONArray) { @@ -331,8 +332,19 @@ public class AdvFilterParser { if (StringUtils.isBlank(value)) { return null; } - - // 兼容 | 号分割 + + // TIMESTAMP 仅指定了日期值 + if (field.getType() == FieldType.TIMESTAMP && StringUtils.length(value) == 10) { + if ("GT".equalsIgnoreCase(op)) { + value += FULL_TIME; + } else if ("LT".equalsIgnoreCase(op)) { + value += ZERO_TIME; + } else if ("BW".equalsIgnoreCase(op)) { + value += (endVal ? FULL_TIME : ZERO_TIME); + } + } + + // 多个值的情况下,兼容 | 号分割 if (op.equalsIgnoreCase("IN") || op.equalsIgnoreCase("NIN") || op.equalsIgnoreCase("SFD")) { Set inVals = new HashSet<>(); for (String v : value.split("\\|")) { diff --git a/src/main/webapp/assets/js/rb-advfilter.jsx b/src/main/webapp/assets/js/rb-advfilter.jsx index a4194663f..04cd6ff18 100644 --- a/src/main/webapp/assets/js/rb-advfilter.jsx +++ b/src/main/webapp/assets/js/rb-advfilter.jsx @@ -207,7 +207,7 @@ class AdvFilter extends React.Component { } } -const OP_TYPE = { LK: '包含', NLK: '不包含', IN: '包含', NIN: '不包含', EQ: '等于', NEQ: '不等于', GT: '大于', LT: '小于', BW: '区间', NL: '为空', NT: '不为空', BFD: '...天前', BFM: '...月前', AFD: '...天后', AFM: '...月后', RED: '最近...天', REM: '最近...月', SFU: '本人', SFB: '本部门', SFD: '本部门及子部门', YTA:'昨天', TDA:'今天', TTA:'明天' } +const OP_TYPE = { LK: '包含', NLK: '不包含', IN: '包含', NIN: '不包含', EQ: '等于', NEQ: '不等于', GT: '大于', LT: '小于', BW: '区间', NL: '为空', NT: '不为空', BFD: '...天前', BFM: '...月前', AFD: '...天后', AFM: '...月后', RED: '最近...天', REM: '最近...月', SFU: '本人', SFB: '本部门', SFD: '本部门及子部门', YTA: '昨天', TDA: '今天', TTA: '明天' } const OP_DATE_NOPICKER = ['BFD', 'BFM', 'AFD', 'AFM', 'RED', 'REM'] const OP_NOVALUE = ['NL', 'NT', 'SFU', 'SFB', 'SFD', 'YTA', 'TDA', 'TTA'] const PICKLIST_CACHE = {} @@ -259,7 +259,7 @@ class FilterItem extends React.Component { if (fieldType === 'NUMBER' || fieldType === 'DECIMAL') { op = ['GT', 'LT', 'BW', 'EQ'] } else if (fieldType === 'DATE' || fieldType === 'DATETIME') { - op = ['YTA', 'TDA', 'TTA', 'GT', 'LT', 'BW', 'RED', 'REM', 'BFD', 'BFM', 'AFD', 'AFM'] + op = ['TDA', 'YTA', 'TTA', 'GT', 'LT', 'EQ', 'BW', 'RED', 'REM', 'BFD', 'BFM', 'AFD', 'AFM'] } else if (fieldType === 'FILE' || fieldType === 'IMAGE') { op = [] } else if (fieldType === 'PICKLIST') { diff --git a/src/test/java/com/rebuild/server/service/query/AdvFilterParserTest.java b/src/test/java/com/rebuild/server/service/query/AdvFilterParserTest.java index 04f7d839c..5176d4363 100644 --- a/src/test/java/com/rebuild/server/service/query/AdvFilterParserTest.java +++ b/src/test/java/com/rebuild/server/service/query/AdvFilterParserTest.java @@ -62,4 +62,34 @@ public class AdvFilterParserTest extends TestSupport { String where = new AdvFilterParser(filterExp).toSqlWhere(); System.out.println(where); } + + @Test + public void testDateAndDatetime() throws Exception { + JSONObject filterExp = new JSONObject(); + filterExp.put("entity", TEST_ENTITY); + JSONArray items = new JSONArray(); + filterExp.put("items", items); + + // Use `=` + items.add(JSON.parseObject("{ op:'EQ', field:'date', value:'2019-09-09' }")); + // Use `between` + items.add(JSON.parseObject("{ op:'EQ', field:'datetime', value:'2019-09-09' }")); + System.out.println(new AdvFilterParser(filterExp).toSqlWhere()); + + items.clear(); + // Use `=` + items.add(JSON.parseObject("{ op:'TDA', field:'date' }")); + // Use `between` + items.add(JSON.parseObject("{ op:'TDA', field:'datetime' }")); + System.out.println(new AdvFilterParser(filterExp).toSqlWhere()); + + items.clear(); + // No padding + items.add(JSON.parseObject("{ op:'GT', field:'date', value:'2019-09-09' }")); + // Padding time + items.add(JSON.parseObject("{ op:'GT', field:'datetime', value:'2019-09-09' }")); + // No padding + items.add(JSON.parseObject("{ op:'GT', field:'datetime', value:'2019-09-09 12:12:54' }")); + System.out.println(new AdvFilterParser(filterExp).toSqlWhere()); + } }