Springboot + easyui + mybatis 高级搜索功能实现

发布时间:2023年12月29日

最近接了个项目,客户要求项目支持高级搜索,他可以自选字段,然后自选运算符,然后输入值,字段可随意组合,类似于下图。

1 前端处理

遇到问题首先抽象:

字段类型一般有4种,第一种普通输入框,第二种日期,第三种数字,第四种下拉。

如果是下拉的话,只支持精确匹配 =, 其他的支持所有的运算符。

按照我的性格,这个高级搜索功能肯定是要配置出来的,而不是傻乎乎的写html代码。

首先确定配置JSON 文件格式,然后做通用页面,解析配置,配置比较简单,就不多解释了

1
2
3
4
5

var advanceConfig = [{name:'name',title:'人员姓名',type:'input'},
{name:'sex',title:'性别',type:'book',code:'sex'},
{name:'birthday',title:'出生年月',type:'date',formart:'yyyy-MM'},
{name:'primaryClassification',title:'一级分类',type:'book',code:'primary_classification'},
];

然后写公共代码。

<script type="text/javascript" src="${fhs_static_url}js/baidutemplate.js"></script>
<script type="text/javascript"
? ? ? ? src="${fhs_static_url}js/My97DatePicker/WdatePicker.js"></script>
<script>


? ? //记录每个index是类型是什么
? ? var indexType = {};

? ? var advnaceIsInit= false;

? ? var filter_index = 0;


? ? function openAdvance(){
? ? ? ? advanceConfig = advanceConfig.filter(function(val){
? ? ? ? ? ? return !(!val || val === "");
? ? ? ? });
? ? ? ? for(i=0;i<advanceConfig.length;i++){
? ? ? ? ? ? if(advanceConfig[i]){
? ? ? ? ? ? ? ? advanceConfig[i].index = (i+1);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if(!advnaceIsInit){
? ? ? ? ? ? addFilter();
? ? ? ? ? ? advnaceIsInit = true;
? ? ? ? }
? ? ? ? $('#advanceDialog').dialog('open').dialog('setTitle', '高级搜索');
? ? }


? ? //添加一个过滤条件
? ? function addFilter(){
? ? ? ? var _html = baidu.template('rowTemplate',{filterIndex:filter_index});
? ? ? ? $('#advanceFormDiv').append(_html);
? ? ? ? $.parser.parse('#filter' + filter_index);
? ? ? ? filter_index = filter_index + 1;
? ? }


? ? //修改右侧内容
? ? function filterFieldChange(_tempIndex,_record){
? ? ? ? var _tempHtml = '';
? ? ? ? indexType[_tempIndex] = _record;
? ? ? ? var _needSetFilterTypeReadonly = false;
? ? ? ? if(_record.type=='input' || _record.type=='number'){
? ? ? ? ? ? _tempHtml = '<input type="text" id="advanceFilterVal' ?+ _tempIndex + ?'"/>';
? ? ? ? }
? ? ? ? else if(_record.type=='date'){
? ? ? ? ? ? if(!_record.formart){
? ? ? ? ? ? ? ? _record.formart = 'yyyy-MM-dd';
? ? ? ? ? ? }
? ? ? ? ? ? console.log(_record.formart);
? ? ? ? ? ? _tempHtml = '<input type="text" id="advanceFilterVal' ?+ _tempIndex + ?'" readonly οnclick="WdatePicker({dateFmt:\'' + _record.formart + '\'})"/>';
? ? ? ? }
? ? ? ? else if(_record.type=='book'){
? ? ? ? ? ? _tempHtml = '<input type="text" id="advanceFilterVal' ?+ _tempIndex + ?'" ?class="easyui-combobox"' +
? ? ? ? ? ? ? ? ' url="${fhs_basics_url}/webApi/wordbook/getData?wordbookGroupCode=' + _record.code + '&jsonpCallback=?" valueField="wordbookCode" ?textField="wordbookDesc"/>';
? ? ? ? ? ? _needSetFilterTypeReadonly = true;
? ? ? ? }
? ? ? ? else if(_record.type=='select'){
? ? ? ? ? ? _tempHtml = '<input type="text" id="advanceFilterVal' ?+ _tempIndex + ?'" ?class="easyui-combobox"' +
? ? ? ? ? ? ? ? ' url="' + _record.url + '" valueField="'+_record.valueField+'" ?textField="'+_record.textField+'"/>';
? ? ? ? ? ? _needSetFilterTypeReadonly = true;
? ? ? ? }
? ? ? ? if(_record.name=='searchKey'){
? ? ? ? ? ? _needSetFilterTypeReadonly = true;
? ? ? ? }
? ? ? ? if(_needSetFilterTypeReadonly){
? ? ? ? ? ? $('#advanceFilterType' + _tempIndex).combobox('setValue','0');
? ? ? ? ? ? $('#advanceFilterType' + _tempIndex).combobox('readonly',true);
? ? ? ? }else{
? ? ? ? ? ? $('#advanceFilterType' + _tempIndex).combobox('readonly',false);
? ? ? ? }
? ? ? ? if(_record.searchKeyType =='str'){
? ? ? ? ? ? $('#advanceFilterType' + _tempIndex).combobox('setValue','1');
? ? ? ? }
? ? ? ? $('#advanceFilterValLable' + _tempIndex).html(_tempHtml);
? ? ? ? $.parser.parse('#advanceFilterValLable' + _tempIndex);
? ? }


? ? var extAdvanceFilterParam = [];
? ? function ?execAdvanceSearch(){
? ? ? ? extAdvanceFilterParam = [];
? ? ? ? for(i=0;i<filter_index;i++){
? ? ? ? ? ? if($('#advanceFilterField' + i).length>0 && $('#advanceFilterField' + i).combobox('getValue')){
? ? ? ? ? ? ? ? var _val = '';
? ? ? ? ? ? ? ? if(indexType[i].type!=='book' && indexType[i].type!=='select'){
? ? ? ? ? ? ? ? ? ? _val = $('#advanceFilterVal' + i).val();
? ? ? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? ? ? _val = $('#advanceFilterVal' + i).combobox('getValue');
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if(_val){
? ? ? ? ? ? ? ? ? ? extAdvanceFilterParam.push({name:indexType[i].name,val:_val,
? ? ? ? ? ? ? ? ? ? ? ? filterType:$('#advanceFilterType' + i).combobox('getValue'),
? ? ? ? ? ? ? ? ? ? ? ? searchKeyType:indexType[i].searchKeyType,
? ? ? ? ? ? ? ? ? ? ? ? fieldName:indexType[i].fieldName});
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? $('#listGrid').datagrid('load', {
? ? ? ? ? ? extAdvanceFilterParam:JSON.stringify({filterType:$('#advanceFilterType').combobox('getValue'),extAdvanceFilterParamArray:extAdvanceFilterParam})
? ? ? ? });
? ? ? ?//reload();
? ? ? ? console.log(extAdvanceFilterParam);
? ? }



</script>

<script id="rowTemplate" type="text/html">
? ? <div class="fitem" id="filter<@=filterIndex@>">
? ? ? ? <div class='bigLabelDiv'><label>选择字段:</label></div>
? ? ? ? <div class='bigContent'>
? ? ? ? ? ? <select class="easyui-combobox" id="advanceFilterField<@=filterIndex@>" data-options="
? ? ? ? ? ? ? ? ? ? onSelect:function(_record){
? ? ? ? ? ? ? ? ? ? ? ? ? ? var _tempIndex = <@=filterIndex@>;
? ? ? ? ? ? ? ? ? ? ? ? ? ? filterFieldChange(_tempIndex,_record);
? ? ? ? ? ? ? ? ? ? },
? ? ? ? ? ? ? ? ? ? data:advanceConfig,
? ? ? ? ? ? ? ? ? ? valueField:'index',
? ? ? ? ? ? ? ? ? ? textField:'title',
? ? ? ? ? ? ">
? ? ? ? ? ? </select>
? ? ? ? ? ? <select class="easyui-combobox" id="advanceFilterType<@=filterIndex@>">
? ? ? ? ? ? ? ? <option value="0" checked="checked">等于</option>
? ? ? ? ? ? ? ? <option value="1">包含</option>
? ? ? ? ? ? ? ? <option value="2">不等于</option>
? ? ? ? ? ? ? ? <option value="3">大于等于</option>
? ? ? ? ? ? ? ? <option value="4">小于等于</option>
? ? ? ? ? ? ? ? <option value="5">大于</option>
? ? ? ? ? ? ? ? <option value="6">小于</option>
? ? ? ? ? ? ? ? <option value="7">以什么开始</option>
? ? ? ? ? ? ? ? <option value="8">以什么结尾</option>
? ? ? ? ? ? </select>
? ? ? ? ? ? <lable id="advanceFilterValLable<@=filterIndex@>">

? ? ? ? ? ? </lable>
? ? ? ? ? ? <a href="javascript:void(0)" class="easyui-linkbutton" οnclick="$('#filter<@=filterIndex@>').remove()">删除</a>
? ? ? ? </div>
? ? </div>
</script>
<div class="easyui-dialog" id = "advanceDialog" ? style="width: 80%; height: 40%; padding: 10px" closed="true">
? ? <div id="advanceFormDiv">
? ? ? ? <div class="fitem">
? ? ? ? ? ? <div class='bigLabelDiv'><label>过滤条件匹配:</label></div>
? ? ? ? ? ? <div class='bigContent'>
? ? ? ? ? ? ? ? <select class="easyui-combobox" id="advanceFilterType">
? ? ? ? ? ? ? ? ? ? <option value="and" checked="checked">AND(满足所有)</option>
? ? ? ? ? ? ? ? ? ? <option value="or">OR(满足一个)</option>
? ? ? ? ? ? ? ? </select>
? ? ? ? ? ? ? ? <a href="javascript:void(0)" class="easyui-linkbutton" ?οnclick="addFilter()">添加</a>
? ? ? ? ? ? </div>
? ? ? ? </div>
? ? </div>
? ? <div class="fitem">
? ? ? ? <center> ?<a href="javascript:void(0)" class="easyui-linkbutton" ?οnclick="execAdvanceSearch()">搜索</a>
? ? ? ? ? ? <a href="javascript:void(0)" class="easyui-linkbutton" ?οnclick="closeAdvanceSearch()">关闭</a></center>
? ? </div>
</div>


<script>

? ? function closeAdvanceSearch(){
? ? ? ? $('#advanceDialog').dialog('close');
? ? }


</script>

经过上面的代码,前段基本处理完成,下面讲解后端代码如何处理。

2 后端处理

首先在VO中创建接收前段搜索条件的字段。

1
2
3
4
5

?/**
? ? ?* 高级搜索过滤条件
? ? ?*/
? ? @Transient
? ? private String extAdvanceFilterParam;

然后写格式化sql的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

?private static final Map<String, String> SIMPLE_OPERATOR = new HashMap();

? ? static {
? ? ? ? SIMPLE_OPERATOR.put(POJOConstant.EQ, " = ");
? ? ? ? SIMPLE_OPERATOR.put(POJOConstant.LIKE, " LIKE ");
? ? ? ? SIMPLE_OPERATOR.put(POJOConstant.NEQ, " != ");
? ? ? ? SIMPLE_OPERATOR.put(POJOConstant.BIGGER_EQ, " >= ");
? ? ? ? SIMPLE_OPERATOR.put(POJOConstant.LESS_EQ, " <= ");
? ? ? ? SIMPLE_OPERATOR.put(POJOConstant.LESS, " < ");
? ? ? ? SIMPLE_OPERATOR.put(POJOConstant.START_WITH, " LIKE ");
? ? ? ? SIMPLE_OPERATOR.put(POJOConstant.END_WITH, " LIKE ");
? ? ? ? SIMPLE_OPERATOR.put(POJOConstant.BIGGER, " > ");
? ? }

? ? /**
? ? ?* 获取高级搜索的where条件
? ? ?*
? ? ?* @return
? ? ?*/
? ? public String getAdvanceSearchSql() {
? ? ? ? if (extAdvanceFilterParam == null) {
? ? ? ? ? ? return null;
? ? ? ? }
? ? ? ? JSONObject extAdvanceFilterParamJson = JSON.parseObject(extAdvanceFilterParam);
? ? ? ? String filterType = " OR ";
? ? ? ? boolean isOr = true;
? ? ? ? if (extAdvanceFilterParamJson.getString("filterType").equals("and")) {
? ? ? ? ? ? filterType = " AND ";
? ? ? ? ? ? isOr = false;
? ? ? ? }
? ? ? ? JSONArray extAdvanceFilterParamArray = extAdvanceFilterParamJson.getJSONArray("extAdvanceFilterParamArray");
? ? ? ? JSONObject tempAFilter = null;
? ? ? ? Field field = null;
? ? ? ? String sqlField = null;
? ? ? ? String tempVal = null;
? ? ? ? StringBuilder whereSql = new StringBuilder(isOr ? " AND (" : " AND ");
? ? ? ? boolean isHashWhere = false;
? ? ? ? for (int i = 0; i < extAdvanceFilterParamArray.size(); i++) {
? ? ? ? ? ? tempAFilter = extAdvanceFilterParamArray.getJSONObject(i);


? ? ? ? ? ? field = ReflectUtils.getDeclaredField(this.getClass(), tempAFilter.getString("name"));
? ? ? ? ? ? if (field == null) {
? ? ? ? ? ? ? ? log.error("字段不存在:" + field);
? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? }
? ? ? ? ? ? sqlField = getSqlField(field);
? ? ? ? ? ? tempVal = formartVal(field, tempAFilter.get("val"), tempAFilter.getString("filterType"), tempAFilter.getString("searchKeyType")
? ? ? ? ? ? ? ? ? ? , tempAFilter.getString("fieldName"));
? ? ? ? ? ? if (sqlField == null || tempVal == null || !SIMPLE_OPERATOR.containsKey(tempAFilter.getString("filterType"))) {
? ? ? ? ? ? ? ? log.error("条件不满足,无法拼接此字段,详情请打断点:" + field);
? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? }
? ? ? ? ? ? if (whereSql.length() > 6) {
? ? ? ? ? ? ? ? whereSql.append(filterType);
? ? ? ? ? ? }
? ? ? ? ? ? whereSql.append(sqlField + ("searchKey".equals(field.getName()) ? " LIKE " : SIMPLE_OPERATOR.get(tempAFilter.getString("filterType"))) + tempVal + " ");
? ? ? ? ? ? isHashWhere = true;
? ? ? ? }
? ? ? ? if(!isHashWhere){
? ? ? ? ? ? return "";
? ? ? ? }
? ? ? ? whereSql.append(isOr ? ")" : "");
? ? ? ? return whereSql.toString();
? ? }

? ? /**
? ? ?* 格式化值
? ? ?*
? ? ?* @param field ? ? ?lambdaSett
? ? ?* @param val ? ? ? ?值
? ? ?* @param filterType
? ? ?* @return 值的sql格式
? ? ?*/
? ? protected String formartVal(Field field, Object val, String filterType, String searchKeyType, String fieldName) {
? ? ? ? if (val == null || "null".equals(val)) {
? ? ? ? ? ? return "null";
? ? ? ? }
? ? ? ? Class<?> type = field.getType();
? ? ? ? String result = null;
? ? ? ? // 字符串直接是字段名
? ? ? ? if (!CheckUtils.isNullOrEmpty(searchKeyType)) {
? ? ? ? ? ? if ("str".equals(searchKeyType)) {
? ? ? ? ? ? ? ? return " CONCAT('%','"" + fieldName + ""','%','" + val + "','%') ";
? ? ? ? ? ? } if ("streq".equals(searchKeyType)) {
? ? ? ? ? ? ? ? return " CONCAT('%','"" + fieldName + "":"" + val + ""','%') ";
? ? ? ? ? ? } else if ("date".equals(searchKeyType)) {
? ? ? ? ? ? ? ? return " CONCAT('%','"" + fieldName + "":"" + val + "','%') ";
? ? ? ? ? ? } else if ("int".equals(searchKeyType)) {
? ? ? ? ? ? ? ? return " CONCAT('%','"" + fieldName + "":"" + val + ""','%') ";
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? //只有字符串才有like 需要特殊处理
? ? ? ? if (type == String.class) {
? ? ? ? ? ? if (POJOConstant.LIKE.equals(filterType)) {
? ? ? ? ? ? ? ? result = " CONCAT('%','" + val + "','%') ";
? ? ? ? ? ? } else if (POJOConstant.START_WITH.equals(filterType)) {
? ? ? ? ? ? ? ? result = " CONCAT('" + val + "','%') ";
? ? ? ? ? ? } else if (POJOConstant.END_WITH.equals(filterType)) {
? ? ? ? ? ? ? ? result = " CONCAT('%','" + val + "') ";
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? result = "'" + val + "'";
? ? ? ? ? ? }
? ? ? ? } else if (type == Number.class || Number.class.isAssignableFrom(type)) {
? ? ? ? ? ? result = ConverterUtils.toString(val);
? ? ? ? } else if (type == Date.class || Date.class.isAssignableFrom(type)) {
? ? ? ? ? ? Date dateVal = DateUtils.parseStr(ConverterUtils.toString(val));
? ? ? ? ? ? return "FROM_UNIXTIME(" + (dateVal.getTime() / 1000) + ")";
? ? ? ? } else {
? ? ? ? ? ? log.warn("格式不支持:" + field + val);
? ? ? ? ? ? return null;
? ? ? ? }
? ? ? ? return result;
? ? }


? ? public String getSqlField(Field field) {
? ? ? ? if (field.isAnnotationPresent(TableField.class)) {
? ? ? ? ? ? TableField tableField = field.getAnnotation(TableField.class);
? ? ? ? ? ? if (tableField.exist()) {
? ? ? ? ? ? ? ? return tableField.value();
? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? return null;
? ? ? ? ? ? }
? ? ? ? } else if (field.isAnnotationPresent(Column.class)) {
? ? ? ? ? ? Column column = field.getAnnotation(Column.class);
? ? ? ? ? ? return column.name();
? ? ? ? }
? ? ? ? return null;
? ? }

通过上面代码,基本上已经把sql拼接出来了,如果觉得不安全可以加个sql的过滤,如果有人故意黑,可以抛异常。

有了sql 之后拼到查询语句中就可以了。

文章来源:https://blog.csdn.net/weixin_42023748/article/details/135288960
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。