fxi(功能用例): 高级搜索部分条件查询有误

--task=1016128 --user=陈建星 高级搜索-视图增删改查-后端 https://www.tapd.cn/55049933/s/1573254
This commit is contained in:
AgAngle 2024-09-12 10:42:46 +08:00 committed by Craftsman
parent 8a9808f541
commit c0400c483d
8 changed files with 213 additions and 84 deletions

View File

@ -1,10 +1,12 @@
package io.metersphere.sdk.dto; package io.metersphere.sdk.dto;
import io.metersphere.sdk.constants.CustomFieldType;
import io.metersphere.sdk.valid.EnumValue; import io.metersphere.sdk.valid.EnumValue;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.List; import java.util.List;
@ -35,6 +37,19 @@ public class CombineCondition {
@EnumValue(enumClass = CombineConditionOperator.class) @EnumValue(enumClass = CombineConditionOperator.class)
private String operator; private String operator;
/**
* 是否是多选自定义字段
* BaseMapper.xml 中调用
* @return
*/
public Boolean isMultipleCustomField() {
if (BooleanUtils.isTrue(customField)) {
return StringUtils.equalsAny(customFieldType, CustomFieldType.MULTIPLE_SELECT.name(),
CustomFieldType.MULTIPLE_INPUT.name(), CustomFieldType.MULTIPLE_MEMBER.name(), CustomFieldType.CHECKBOX.name());
}
return false;
}
public boolean valid() { public boolean valid() {
if (StringUtils.isBlank(name) || StringUtils.isBlank(operator)) { if (StringUtils.isBlank(name) || StringUtils.isBlank(operator)) {
return false; return false;

View File

@ -373,13 +373,13 @@
</include> </include>
</if> </if>
<if test="condition.name == 'follower'"> <if test="condition.name == 'follower'">
api_definition.id in ( <include refid="io.metersphere.system.mapper.BaseMapper.associationCondition">
select api_definition_id from api_definition_follower where <property name="mainIdColumn" value="api_definition.id"/>
<include refid="io.metersphere.system.mapper.BaseMapper.condition"> <property name="associationTable" value="api_definition_follower"/>
<property name="associationIdColumn" value="api_definition_id"/>
<property name="searchColumn" value="user_id"/>
<property name="condition" value="condition"/> <property name="condition" value="condition"/>
<property name="column" value="user_id"/>
</include> </include>
)
</if> </if>
<include refid="io.metersphere.system.mapper.BaseMapper.queryType"> <include refid="io.metersphere.system.mapper.BaseMapper.queryType">
<property name="searchMode" value="${combineSearch}.searchMode"/> <property name="searchMode" value="${combineSearch}.searchMode"/>
@ -418,15 +418,12 @@
<property name="searchMode" value="${combineSearch}.searchMode"/> <property name="searchMode" value="${combineSearch}.searchMode"/>
</include> </include>
</foreach> </foreach>
<if test="${combineSearch}.customFiledConditions.size() > 0"> <include refid="io.metersphere.system.mapper.BaseMapper.customFiledConditions">
api_definition.id in ( <property name="mainIdColumn" value="api_definition.id"/>
select api_id from api_definition_custom_field where <property name="associationTable" value="api_definition_custom_field"/>
<include refid="io.metersphere.system.mapper.BaseMapper.customFiledConditions"> <property name="associationIdColumn" value="api_id"/>
<property name="conditions" value="${combineSearch}.customFiledConditions"/> <property name="combineSearch" value="${combineSearch}"/>
<property name="searchMode" value="${combineSearch}.searchMode"/> </include>
</include>
)
</if>
</if> </if>
</trim> </trim>
</sql> </sql>

View File

@ -144,16 +144,14 @@
<property name="column" value="functional_case.create_user"/> <property name="column" value="functional_case.create_user"/>
</include> </include>
</if> </if>
<if test="condition.name == 'reviewUser'"> <if test="condition.name == 'reviewers'">
crfc.case_id in ( <include refid="io.metersphere.system.mapper.BaseMapper.associationCondition">
select case_id from case_review_functional_case_user <property name="mainIdColumn" value="crfc.case_id"/>
where review_id = #{request.reviewId} <property name="associationTable" value="case_review_functional_case_user"/>
and <property name="associationIdColumn" value="case_id"/>
<include refid="io.metersphere.system.mapper.BaseMapper.condition"> <property name="searchColumn" value="user_id"/>
<property name="condition" value="condition"/> <property name="condition" value="condition"/>
<property name="column" value="user_id"/>
</include> </include>
)
</if> </if>
<include refid="io.metersphere.system.mapper.BaseMapper.queryType"> <include refid="io.metersphere.system.mapper.BaseMapper.queryType">
<property name="searchMode" value="${combineSearch}.searchMode"/> <property name="searchMode" value="${combineSearch}.searchMode"/>
@ -170,37 +168,40 @@
<property name="column" value="crfc.status"/> <property name="column" value="crfc.status"/>
</include> </include>
</if> </if>
<if test="condition.name == 'demand'"> <if test="condition.name == 'lastExecuteResult'">
functional_case.id in (
select case_id from functional_case_demand where
<include refid="io.metersphere.system.mapper.BaseMapper.condition"> <include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="condition" value="condition"/> <property name="condition" value="condition"/>
<property name="column" value="demand_name"/> <property name="column" value="functional_case.last_execute_result"/>
</include>
</if>
<if test="condition.name == 'demand'">
<include refid="io.metersphere.system.mapper.BaseMapper.associationCondition">
<property name="mainIdColumn" value="functional_case.id"/>
<property name="associationTable" value="functional_case_demand"/>
<property name="associationIdColumn" value="case_id"/>
<property name="searchColumn" value="demand_name"/>
<property name="condition" value="condition"/>
</include> </include>
)
</if> </if>
<if test="condition.name == 'attachment'"> <if test="condition.name == 'attachment'">
functional_case.id in ( <include refid="io.metersphere.system.mapper.BaseMapper.associationCondition">
select case_id from functional_case_attachment where <property name="mainIdColumn" value="functional_case.id"/>
<include refid="io.metersphere.system.mapper.BaseMapper.condition"> <property name="associationTable" value="functional_case_attachment"/>
<property name="associationIdColumn" value="case_id"/>
<property name="searchColumn" value="file_name"/>
<property name="condition" value="condition"/> <property name="condition" value="condition"/>
<property name="column" value="file_name"/>
</include> </include>
)
</if> </if>
<include refid="io.metersphere.system.mapper.BaseMapper.queryType"> <include refid="io.metersphere.system.mapper.BaseMapper.queryType">
<property name="searchMode" value="${combineSearch}.searchMode"/> <property name="searchMode" value="${combineSearch}.searchMode"/>
</include> </include>
</foreach> </foreach>
<if test="${combineSearch}.customFiledConditions.size() > 0"> <include refid="io.metersphere.system.mapper.BaseMapper.customFiledConditions">
functional_case.id in ( <property name="mainIdColumn" value="functional_case.id"/>
select case_id from functional_case_custom_field where <property name="associationTable" value="functional_case_custom_field"/>
<include refid="io.metersphere.system.mapper.BaseMapper.customFiledConditions"> <property name="associationIdColumn" value="case_id"/>
<property name="conditions" value="${combineSearch}.customFiledConditions"/> <property name="combineSearch" value="${combineSearch}"/>
<property name="searchMode" value="${combineSearch}.searchMode"/> </include>
</include>
)
</if>
</if> </if>
</trim> </trim>
</sql> </sql>

View File

@ -292,13 +292,13 @@
</include> </include>
</if> </if>
<if test="condition.name == 'follower'"> <if test="condition.name == 'follower'">
functional_case.id in ( <include refid="io.metersphere.system.mapper.BaseMapper.associationCondition">
select case_id from functional_case_follower where <property name="mainIdColumn" value="functional_case.id"/>
<include refid="io.metersphere.system.mapper.BaseMapper.condition"> <property name="associationTable" value="functional_case_follower"/>
<property name="condition" value="condition"/> <property name="associationIdColumn" value="case_id"/>
<property name="column" value="user_id"/> <property name="searchColumn" value="user_id"/>
</include> <property name="condition" value="condition"/>
) </include>
</if> </if>
<include refid="io.metersphere.system.mapper.BaseMapper.queryType"> <include refid="io.metersphere.system.mapper.BaseMapper.queryType">
<property name="searchMode" value="${combineSearch}.searchMode"/> <property name="searchMode" value="${combineSearch}.searchMode"/>
@ -322,36 +322,33 @@
</include> </include>
</if> </if>
<if test="condition.name == 'demand'"> <if test="condition.name == 'demand'">
functional_case.id in ( <include refid="io.metersphere.system.mapper.BaseMapper.associationCondition">
select case_id from functional_case_demand where <property name="mainIdColumn" value="functional_case.id"/>
<include refid="io.metersphere.system.mapper.BaseMapper.condition"> <property name="associationTable" value="functional_case_demand"/>
<property name="condition" value="condition"/> <property name="associationIdColumn" value="case_id"/>
<property name="column" value="demand_name"/> <property name="searchColumn" value="demand_name"/>
</include> <property name="condition" value="condition"/>
) </include>
</if> </if>
<if test="condition.name == 'attachment'"> <if test="condition.name == 'attachment'">
functional_case.id in ( <include refid="io.metersphere.system.mapper.BaseMapper.associationCondition">
select case_id from functional_case_attachment where <property name="mainIdColumn" value="functional_case.id"/>
<include refid="io.metersphere.system.mapper.BaseMapper.condition"> <property name="associationTable" value="functional_case_attachment"/>
<property name="associationIdColumn" value="case_id"/>
<property name="searchColumn" value="file_name"/>
<property name="condition" value="condition"/> <property name="condition" value="condition"/>
<property name="column" value="file_name"/>
</include> </include>
)
</if> </if>
<include refid="io.metersphere.system.mapper.BaseMapper.queryType"> <include refid="io.metersphere.system.mapper.BaseMapper.queryType">
<property name="searchMode" value="${combineSearch}.searchMode"/> <property name="searchMode" value="${combineSearch}.searchMode"/>
</include> </include>
</foreach> </foreach>
<if test="${combineSearch}.customFiledConditions.size() > 0"> <include refid="io.metersphere.system.mapper.BaseMapper.customFiledConditions">
functional_case.id in ( <property name="mainIdColumn" value="functional_case.id"/>
select case_id from functional_case_custom_field where <property name="associationTable" value="functional_case_custom_field"/>
<include refid="io.metersphere.system.mapper.BaseMapper.customFiledConditions"> <property name="associationIdColumn" value="case_id"/>
<property name="conditions" value="${combineSearch}.customFiledConditions"/> <property name="combineSearch" value="${combineSearch}"/>
<property name="searchMode" value="${combineSearch}.searchMode"/> </include>
</include>
)
</if>
</if> </if>
</trim> </trim>
</sql> </sql>
@ -394,7 +391,6 @@
group by ref_id group by ref_id
</select> </select>
<update id="batchDelete"> <update id="batchDelete">
update functional_case update functional_case
set deleted = 1, set deleted = 1,

View File

@ -39,7 +39,7 @@ public enum InternalUserView {
MY_REVIEW(() -> { MY_REVIEW(() -> {
UserViewDTO userViewDTO = getUserViewDTO("my_review"); UserViewDTO userViewDTO = getUserViewDTO("my_review");
CombineCondition condition = new CombineCondition(); CombineCondition condition = new CombineCondition();
condition.setName("reviewUser"); condition.setName("reviewers");
condition.setValue(getCurrentUserArrayValue()); condition.setValue(getCurrentUserArrayValue());
condition.setOperator(CombineCondition.CombineConditionOperator.IN.name()); condition.setOperator(CombineCondition.CombineConditionOperator.IN.name());
userViewDTO.setConditions(List.of(condition)); userViewDTO.setConditions(List.of(condition));

View File

@ -23,7 +23,15 @@ public class DBCombineSearch extends CombineSearch {
*/ */
private List<CombineCondition> systemFieldConditions; private List<CombineCondition> systemFieldConditions;
/** /**
* 自定义字段筛选条件 * 自定义字段为空的筛选条件
*/
private List<CombineCondition> customFiledEmptyConditions;
/**
* 自定义字段中 NOT_IN NOT_EQUALS NOT_CONTAINS COUNT_LT 的条件
*/
private List<CombineCondition> customFiledNoneConditions;
/**
* 其他的自定义字段筛选条件
*/ */
private List<CombineCondition> customFiledConditions; private List<CombineCondition> customFiledConditions;
} }

View File

@ -18,7 +18,10 @@ import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
/** /**
* @Author: jianxing * @Author: jianxing
@ -63,13 +66,40 @@ public class BaseConditionFilterAspect {
} }
List<CombineCondition> validConditions = getValidConditions(combineSearch.getConditions()); List<CombineCondition> validConditions = getValidConditions(combineSearch.getConditions());
replaceCurrentUser(validConditions); replaceCurrentUser(validConditions);
List<CombineCondition> systemFieldConditions = validConditions.stream().filter(item -> !BooleanUtils.isTrue(item.getCustomField())).toList(); List<CombineCondition> systemFieldConditions = validConditions.stream()
List<CombineCondition> customFieldConditions = validConditions.stream().filter(item -> BooleanUtils.isTrue(item.getCustomField())).toList(); .filter(item -> !BooleanUtils.isTrue(item.getCustomField()))
.toList();
List<CombineCondition> customFieldConditions = validConditions.stream()
.filter(item -> BooleanUtils.isTrue(item.getCustomField()))
.collect(Collectors.toList());
// 拆分系统字段和自定义字段 // 拆分系统字段和自定义字段
DBCombineSearch dbCombineSearch = BeanUtils.copyBean(new DBCombineSearch(), combineSearch); DBCombineSearch dbCombineSearch = BeanUtils.copyBean(new DBCombineSearch(), combineSearch);
dbCombineSearch.setSystemFieldConditions(systemFieldConditions); dbCombineSearch.setSystemFieldConditions(systemFieldConditions);
// 拆分自定义字段操作符为空的条件
List<CombineCondition> customFiledEmptyConditions = new ArrayList<>(0);
// 拆分自定义字段操作符为 NOT_IN NOT_EQUALS NOT_CONTAINS 的条件
List<CombineCondition> customFiledNoneConditions = new ArrayList<>(0);
Iterator<CombineCondition> iterator = customFieldConditions.iterator();
while (iterator.hasNext()) {
CombineCondition customFieldCondition = iterator.next();
if (StringUtils.equalsAny(customFieldCondition.getOperator(), CombineCondition.CombineConditionOperator.EMPTY.name())) {
customFiledEmptyConditions.add(customFieldCondition);
iterator.remove();
} else if (StringUtils.equalsAny(customFieldCondition.getOperator(),
CombineCondition.CombineConditionOperator.COUNT_LT.name(),
CombineCondition.CombineConditionOperator.NOT_IN.name(),
CombineCondition.CombineConditionOperator.NOT_EQUALS.name(),
CombineCondition.CombineConditionOperator.NOT_CONTAINS.name())) {
customFiledNoneConditions.add(customFieldCondition);
iterator.remove();
}
}
dbCombineSearch.setCustomFiledEmptyConditions(customFiledEmptyConditions);
dbCombineSearch.setCustomFiledNoneConditions(customFiledNoneConditions);
dbCombineSearch.setCustomFiledConditions(customFieldConditions); dbCombineSearch.setCustomFiledConditions(customFieldConditions);
baseCondition.setCombineSearch(dbCombineSearch); baseCondition.setCombineSearch(dbCombineSearch);
// 处理视图查询条件 // 处理视图查询条件

View File

@ -54,13 +54,37 @@
</sql> </sql>
<sql id="customFiledConditions"> <sql id="customFiledConditions">
<trim suffixOverrides="AND|OR"> <if test="${combineSearch}.customFiledEmptyConditions.size() > 0">
<foreach collection="${conditions}" item="condition"> <foreach collection="${combineSearch}.customFiledEmptyConditions" item="condition">
<trim prefix="(" suffix=")"> ${mainIdColumn} not in (
field_id = #{condition.name} select ${associationIdColumn} from ${associationTable} where
and field_id = #{condition.name} and
`value` is not null and `value` != ''
<if test="condition.customFieldType != null and condition.isMultipleCustomField()">
and `value` != '[]'
</if>
)
<include refid="io.metersphere.system.mapper.BaseMapper.queryType">
<property name="searchMode" value="${combineSearch}.searchMode"/>
</include>
</foreach>
</if>
<if test="${combineSearch}.customFiledNoneConditions.size() > 0">
<foreach collection="${combineSearch}.customFiledNoneConditions" item="condition">
${mainIdColumn} not in (
select ${associationIdColumn} from ${associationTable} where
field_id = #{condition.name} and
`value` is not null and `value` != ''
<if test="condition.customFieldType != null and condition.isMultipleCustomField()">
and `value` != '[]'
</if>
) or
${mainIdColumn} in (
select ${associationIdColumn} from ${associationTable} where
field_id = #{condition.name} and
<choose> <choose>
<when test="condition.customFieldType != null and (condition.customFieldType == 'MULTIPLE_SELECT' or condition.customFieldType == 'MULTIPLE_SELECT' or condition.customFieldType == 'MULTIPLE_INPUT')"> <when test="condition.customFieldType != null and condition.isMultipleCustomField()">
<include refid="io.metersphere.system.mapper.BaseMapper.arrayValueCondition"> <include refid="io.metersphere.system.mapper.BaseMapper.arrayValueCondition">
<property name="condition" value="condition"/> <property name="condition" value="condition"/>
<property name="column" value="value"/> <property name="column" value="value"/>
@ -73,12 +97,42 @@
</include> </include>
</otherwise> </otherwise>
</choose> </choose>
</trim> )
<include refid="io.metersphere.system.mapper.BaseMapper.queryType"> <include refid="io.metersphere.system.mapper.BaseMapper.queryType">
<property name="searchMode" value="${searchMode}"/> <property name="searchMode" value="${combineSearch}.searchMode"/>
</include> </include>
</foreach> </foreach>
</trim> </if>
<if test="${combineSearch}.customFiledConditions.size() > 0">
${mainIdColumn} in (
select ${associationIdColumn} from ${associationTable} where
<trim suffixOverrides="AND|OR">
<foreach collection="${combineSearch}.customFiledConditions" item="condition">
<trim prefix="(" suffix=")">
field_id = #{condition.name} and
<choose>
<when test="condition.customFieldType != null and condition.isMultipleCustomField()">
<include refid="io.metersphere.system.mapper.BaseMapper.arrayValueCondition">
<property name="condition" value="condition"/>
<property name="column" value="value"/>
</include>
</when>
<otherwise>
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="condition" value="condition"/>
<property name="column" value="value"/>
</include>
</otherwise>
</choose>
</trim>
<include refid="io.metersphere.system.mapper.BaseMapper.queryType">
<property name="searchMode" value="${combineSearch}.searchMode"/>
</include>
</foreach>
</trim>
)
</if>
</sql> </sql>
<sql id="arrayValueCondition"> <sql id="arrayValueCondition">
@ -117,12 +171,40 @@
${column} is null or ${column} = '[]' ${column} is null or ${column} = '[]'
</when> </when>
<when test="${condition}.operator == 'NOT_EMPTY'"> <when test="${condition}.operator == 'NOT_EMPTY'">
${column} is not null and ${column} = '[]' ${column} is not null and ${column} != '[]'
</when> </when>
</choose> </choose>
</trim> </trim>
</sql> </sql>
<sql id="associationCondition">
<choose>
<when test="${condition}.operator == 'EMPTY'">
${mainIdColumn} not in (
select ${associationIdColumn} from ${associationTable} where
${searchColumn} is not null and ${searchColumn} != ''
)
</when>
<otherwise>
(
<if test="${condition}.operator == 'NOT_IN' or ${condition}.operator == 'NOT_EQUALS' or ${condition}.operator == 'NOT_CONTAINS'">
${mainIdColumn} not in (
select ${associationIdColumn} from ${associationTable} where
${searchColumn} is not null and ${searchColumn} != ''
) or
</if>
${mainIdColumn} in (
select ${associationIdColumn} from ${associationTable} where
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="condition" value="${condition}"/>
<property name="column" value="${searchColumn}"/>
</include>
)
)
</otherwise>
</choose>
</sql>
<sql id="condition"> <sql id="condition">
<trim prefix="(" suffix=")"> <trim prefix="(" suffix=")">
<choose> <choose>