需求背景
传统业务方案都是通过对业务需求创建关系表,但是针对的是固定业务结构,比如(业务记录ID、创建人、创建时间、更新人、更新时间、业务相关字段等),要想在此进行扩展字段,就需要对相应的业务表尽心添加字段。
但是这些字段都是基于固定的业务进行的添加,无法让用户按照自身需求进行自定义自己的面板数据。
当然,可能对于简单的场景,可以利用mysql 数据可以定义Json/longText 类型的一个字段让用户自定义的字段,放在这个字段上(存放结构好的字段数据),也是可以的。
下面说的一种方案是应用于复杂场景的:
需求:
1、对于不同视图看板,可以对不同字段进行设置是否展示(同一条数据);
2、对自定义字段可以应用排序条件进行排序;
3、对所有字段的展示顺序可以拖拽自定义字段顺序;
4、设置不同类型的字段(文本、数字、日期、超链接、图片、人员、单选、多选等);
5、每条记录可以进行拖拽排序;
6、设置每个视图的每个字段的页面展示宽度。
7、数据可以同步到别的数据频道下。
8、字段不能创建重复的(名称、字段属性)
对于这些多变化的需求,固定表结构的方式不适合这种需求场景。
实践方案
对于以上需求可以知道:
1、一条数据有不同的展示方式(可以控制字段是否显示)
2、有多种字段类型,每个字段类型需要不同的处理方式(排序、格式化数据)
3、同一个数据可以同步到别的数据频道下(对于目标数据频道不存在的字段进行新增)
传统固定字段的横向结构不能满足自定义字段的复杂需求。需要把数据打散变成纵向表结构
构建相关表结构:
1、字段表: 字段ID、字段名称、字段类型、字段属性(是否自定义、是否固定、选项[单多选]、格式化样式[数字、日期])
2、视图表:视图ID、视图名称、视图排序位置、视图类型
3、视图-字段关联表:视图ID、字段ID、是否展示、字段顺序
4、元数据表:字段ID、记录ID、字段值(longText)
5、频道-记录表:频道ID、记录ID、记录顺序,原频道ID(记录原数据所属频道)、是否同步关联
伪代码
相关实体类
字段表DAO
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DynamicField extends DynamicBaseEntity {
/**
* 主键
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 字段ID
*/
private String fieldId;
/**
* 字段名称
*/
private String name;
/**
* 字段类型
* 1:多行文本
* 2:数字
* 3:单选
* 4:多选
* 5:日期
* 7:复选框
* 11:人员
* 15:超链接
* 17:附件
* 18:单向关联
* #19:查找引用
* #20:公式
* #21:双向关联
* 22:评论
* 1001:创建时间
* 1002:最后更新时间
* 1003:创建人
* 1004:修改人
* 1005:标记时间
*/
@Builder.Default
private int fieldType = 1;
/**
* 是否固定字段
* 固定字段:标题字段,不可移动、修改
*/
private boolean isFixed;
/**
* 是否自定义:用于区分默认字段
*/
private boolean isCustom;
/**
* 字段属性
*/
@TableField(typeHandler = FieldPropertyHandler.class)
private FieldProperty property;
/**
* 字段校验隔离级别
* 所属空间ID
*/
private String spaceId;
}
视图表DAO
@Data
@TableName(autoResultMap = true)
public class DynamicView extends DynamicBaseEntity {
/**
* 视图ID
*/
private String id;
/**
* 视图名称
*/
private String name;
/**
* 视图类型
*/
private ViewTypeEnum viewType;
/**
* 所属空间ID
*/
private String spaceId;
/**
* 频道ID
*/
private String channelId;
/**
* 排序
*/
private Integer indexKey;
/**
* 视图页面属性:
*
*/
@TableField(typeHandler = ViewPropertyHandler.class)
private ViewProperty viewProperty = new ViewProperty();
/**
* 排序规则
*/
@TableField(typeHandler = ListSortHandler.class, javaType = true)
private List<DynamicSort> sortList;
/**
* 筛选规则
*/
@TableField(typeHandler = ListConditionHandler.class, javaType = true)
private List<DynamicCondition> conditions;
/**
* 符合条件规则
* 默认:{@link RuleEnum#ALL}
*/
private RuleEnum rule = RuleEnum.ALL;
/**
* 分组规则
*/
@TableField(typeHandler = ListSortHandler.class, javaType = true)
private List<DynamicSort> groupList;
public boolean isNull() {
if (StringUtils.isEmpty(this.id)) {
return true;
}
return false;
}
}
元数据表DAO
@Data
public class MetaDataTable {
/**
* 数据ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 数据记录ID
*/
private String recordId;
/**
* 字段ID
*/
private String fieldId;
/**
* 字段值:Json
*/
private String value;
}
视图-字段关联表DAO
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ViewFieldRelation extends DynamicBaseEntity {
/**
* 关联数据ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 字段ID
*/
private String fieldId;
/**
* 所属空间ID
*/
private String spaceId;
/**
* 频道ID
*/
private String channelId;
/**
* 视图ID
*/
private String viewId;
/**
* 是否显示
*/
@Builder.Default
private boolean isShow = true;
/**
* 字段展示顺序
*/
private int indexKey;
/**
* 字段排版长度
*/
private Integer fieldLength;
}
记录-频道关联表DAO
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ChannelRecordRelation {
/**
* 关联数据ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 频道ID
*/
private String channelId;
/**
* 数据记录ID
*/
private String recordId;
/**
* 原数据记录所属频道ID
*/
private String originId;
/**
* 是否是共享数据
* 默认:false
*/
private boolean isShare;
/**
* 频道数据展示顺序
*/
private int indexKey;
/**
* 字段校验隔离级别
* 所属空间ID
*/
private String spaceId;
@JsonFormat(locale="zh", timezone="GMT+8")
private Date createdAt;
}
相关类
动态筛选条件类
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DynamicCondition {
/**
* 筛选字段ID
*/
private String fieldId;
/**
* 匹配规则
*/
private MatchRuleEnum matchRule;
/**
* 匹配值
*/
private Set<String> matchValue;
}
动态排序条件类
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DynamicSort {
/**
* 排序字段ID
*/
private String fieldId;
/**
* 排序规则
*/
private SortEnum sort;
/**
* 是否默认固定,不显示
*/
private boolean isFixed;
}
字段属性类
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class FieldProperty {
/**
* 字段图标
*/
private String icon;
/**
* 数字格式化格式:泛型
*/
private NumberTypeEnum formatter;
/**
* 日期格式化:泛型
*/
private DateTypeEnum dateFormatter;
/**
* 是否允许添加多成员
*/
private Boolean addMore;
/**
* 单选、多选:选项属性
*/
private List<Option> options;
}
选项类
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Option {
/**
* 选项ID
*/
private String id;
/**
* 选项值
*/
private String value;
/**
* 选项颜色:色号
*/
private String color;
}
超链接类
@Data
public class HyperlinkEntity {
/**
* 链接
*/
private String link;
/**
* 链接值
*/
private String linkValue;
}
视图属性类
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ViewProperty {
/**
* 行高:
* 0-低,默认
* 1-中等
* 2-高
* 3-超高
*/
private int rowHeight;
/**
* 封面:
* 绑定附件字段ID
*/
private String cover;
/**
* 封面效果:
* 0-无
* 1-剪裁;
* 2-适应
*/
private int coverEffect;
/**
* 展示模式
* 1-常规
* 2-紧凑
*/
@Builder.Default
private int displayMode = 1;
/**
* 是否展示字段名: 默认展示
*/
private boolean isShowFieldName = true;
/**
* 颜色显示: 是否自定义,默认否
*/
private boolean isCustom;
/**
* 颜色:色号
*/
private String color;
/**
* 跟随字段颜色时选定的字段ID及选项
* 格式:filedId&&id
*/
private String colorId;
}
相关枚举
日期枚举类
public enum DateTypeEnum {
/**
* 年/月/日
*/
DATE_ONE("yyyy/MM/dd"),
/**
* 年/月/日 时:分
*/
DATE_TWO("yyyy/MM/dd HH:mm"),
/**
* 年-月-日
*/
DATE_THREE("yyyy-MM-dd"),
/**
* 年-月-日 时:分
*/
DATE_FOUR("yyyy-MM-dd HH:mm"),
/**
* 月-日
*/
DATE_FIVE("MM-dd"),
/**
* 月/日/年
*/
DATE_SIX("MM/dd/yyyy"),
/**
* 日/月/年
*/
DATE_SEVEN("dd/MM/yyyy"),
;
private String format;
DateTypeEnum(String format) {
this.format = format;
}
public String getFormat() {
return this.format;
}
}
数字格式化枚举类
public enum NumberTypeEnum {
/**
* 整数
*/
INTEGER("0"),
/**
* 保留1位小数
*/
KEEP_ONE("0.0"),
/**
* 保留2位小数
*/
KEEP_TWO("0.00"),
/**
* 保留3位小数
*/
KEEP_THREE("0.000"),
/**
* 保留4位小数
*/
KEEP_FOUR("0.0000"),
/**
* 千分位
*/
THOUSANDTH("###,###"),
/**
* 千分位(保留两位小数点)
*/
THOUSANDTH_KEEP_TWO("###,###.00"),
/**
* 百分比
*/
PERCENTAGE("###,###%"),
/**
* 百分比(保留两位小数点)
*/
PERCENTAGE_KEEP_TWO("###,###.00%"),
/**
* 人民币
*/
RMB("¥###,###"),
/**
* 人民币(保留两位小数点)
*/
RMB_KEEP_TWO("¥###,###.00"),
/**
* 美元
*/
DOLLAR("$###,###"),
/**
* 美元(保留两位小数点)
*/
DOLLAR_KEEP_TWO("$###,###.00"),
;
private String format;
NumberTypeEnum(String format) {
this.format = format;
}
public String getFormat() {
return this.format;
}
}
筛选匹配枚举类
public enum MatchRuleEnum {
/**
* 等于
* 适用字段类型:单选、多选、文本、人员、日期、数字、复选框、超链接
*/
EQUAL,
/**
* 不等于
* 适用字段类型:单选、多选、文本、人员、日期、数字、超链接
*/
NOT_EQUAL,
/**
* 大于
* 适用字段类型:日期、数字
*/
THAN,
/**
* 大于等于
* 适用字段类型:数字
*/
THAN_EQUAL,
/**
* 小于
* 适用字段类型:日期、数字
*/
LESS,
/**
* 小于等于
* 适用字段类型:数字
*/
LESS_EQUAL,
/**
* 包含
* 适用字段类型:单选、多选、文本、人员、超链接
*/
CONTAIN,
/**
* 不包含
* 适用字段类型:单选、多选、文本、人员、超链接
*/
NOT_CONTAIN,
/**
* 为空
* 适用字段类型:单选、多选、文本、人员、日期、附件、数字、超链接
*/
EMPTY,
/**
* 不为空
* 适用字段类型:单选、多选、文本、人员、日期、附件、数字、超链接
*/
NOT_EMPTY,
;
}
筛选条件应用规则枚举类
public enum RuleEnum {
/**
* 所有
*/
ALL,
/**
* 任一
*/
ANY
;
}
排序枚举类
public enum SortEnum {
/**
* 正序
*/
ASC,
/**
* 倒序
*/
DESC
;
}
视图类型枚举类
public enum ViewTypeEnum {
/**
* 列表视图
*/
LIST("列表"),
/**
* 看板视图
*/
BOARD("看板"),
;
private String viewName;
ViewTypeEnum(String viewName) {
this.viewName = viewName;
}
public String getViewName() {
return this.viewName;
}
}
自定义排序类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RecordCompareVo implements Comparator<RecordVo> {
/**
* 视图
*/
private List<DynamicSort> sorts;
@Override
public int compare(RecordVo o1, RecordVo o2) {
if (CollectionUtils.isEmpty(sorts)) {
sorts = new ArrayList<>();
}
//默认排序
DynamicSort indexKey = new DynamicSort();
indexKey.setFieldId("indexKey");
indexKey.setSort(SortEnum.ASC);
sorts.add(indexKey);
Map<String, MetaDataTableVo> o1Maps = new HashMap<>();
Map<String, MetaDataTableVo> o2Maps = new HashMap<>();
List<MetaDataTableVo> o1List = o1.getData();
List<MetaDataTableVo> o2List = o2.getData();
if (CollectionUtils.isNotEmpty(o1List)) {
o1Maps.putAll(o1List.stream().collect(Collectors.toMap(MetaDataTableVo::getFieldId, m -> m)));
}
if (CollectionUtils.isNotEmpty(o2List)) {
o2Maps.putAll(o2List.stream().collect(Collectors.toMap(MetaDataTableVo::getFieldId, m -> m)));
}
AtomicInteger cr = new AtomicInteger(0);
if (CollectionUtils.isNotEmpty(sorts)) {
sorts.forEach(dynamicSort -> {
if (Objects.equals(cr.get(), 0)) {
if (Objects.equals(dynamicSort.getFieldId(), "indexKey")) {
int sort = o1.getIndexKey() - o2.getIndexKey();
cr.set(Integer.compare(sort, 0));
return;
}
if (o1Maps.containsKey(dynamicSort.getFieldId()) && o2Maps.containsKey(dynamicSort.getFieldId())) {
String o1Value = o1Maps.get(dynamicSort.getFieldId()).getCompareValue();
String o2Value = o2Maps.get(dynamicSort.getFieldId()).getCompareValue();
if (StringUtils.isEmpty(o1Value) && StringUtils.isEmpty(o2Value)) {
cr.set(0);
} else if (StringUtils.isEmpty(o1Value)) {
cr.set(Objects.equals(dynamicSort.getSort(), SortEnum.ASC) ? -1 : 1);
} else if (StringUtils.isEmpty(o2Value)) {
cr.set(Objects.equals(dynamicSort.getSort(), SortEnum.ASC) ? 1 : -1);
} else {
//时间
if (Objects.equals(o1Maps.get(dynamicSort.getFieldId()).getFieldType(), 5)) {
o1Value = o1Value.replace("-", "");
o2Value = o2Value.replace("-", "");
}
int sortValue = Objects.equals(dynamicSort.getSort(), SortEnum.ASC) ? Collator.getInstance(Locale.CHINA).compare(o1Value, o2Value) : Collator.getInstance(Locale.CHINA).compare(o2Value, o1Value);
cr.set(Integer.compare(sortValue, 0));
}
return;
}
if (!o1Maps.containsKey(dynamicSort.getFieldId()) && !o2Maps.containsKey(dynamicSort.getFieldId())) {
return;
}
if (!o1Maps.containsKey(dynamicSort.getFieldId())) {
cr.set(1);
return;
}
if (!o2Maps.containsKey(dynamicSort.getFieldId())) {
cr.set(-1);
return;
}
}
});
}
return cr.get();
}
}
构建记录:筛选排序
public List<RecordVo> buildResults(String spaceId, MetaDataBo bo, DynamicView dynamicView, List<ViewFieldRelationVo> viewFieldRelationVos, List<Map<String, Object>> metaDatas) {
//返回记录
List<RecordVo> recordVoList = new ArrayList<>();
//排序条件
List<DynamicSort> sorts = dynamicView.getSortList();
Map<String, DynamicSort> sortMaps = new HashMap<>();
if (CollectionUtils.isNotEmpty(sorts)) {
sortMaps.putAll(sorts.stream().collect(Collectors.toMap(DynamicSort::getFieldId, s -> s)));
}
if (CollectionUtils.isNotEmpty(metaDatas)) {
//分组字段数据
Map<String, List<Map<String, Object>>> map = metaDatas.stream().collect(Collectors.groupingBy(metaData -> (String) metaData.get("recordId")));
//筛选条件
List<DynamicCondition> conditions = new ArrayList<>();
//添加标题筛选
if (StringUtils.isNotEmpty(bo.getTitle())) {
DynamicCondition title = new DynamicCondition();
title.setFieldId(MultiTableConstant.TITLE_FIELD_ID);
title.setMatchRule(MatchRuleEnum.CONTAIN);
title.setMatchValue(Set.of(bo.getTitle()));
conditions.add(title);
}
Map<String, List<DynamicCondition>> conditionMaps = new HashMap<>();
//判断是否符合筛选条件
if (CollectionUtils.isNotEmpty(dynamicView.getConditions())) {
conditions.addAll(dynamicView.getConditions());
}
if (CollectionUtils.isNotEmpty(conditions)) {
conditionMaps.putAll(conditions.stream().collect(Collectors.groupingBy(DynamicCondition::getFieldId)));
}
map.forEach((recordId, metaDataTables) -> {
AtomicInteger conditionCount = new AtomicInteger(conditions.size());
Set<Boolean> conditionSet = new HashSet<>();
RecordVo recordVo = new RecordVo();
List<MetaDataTableVo> metaDataTableVos = new ArrayList<>();
recordVo.setRecordId(recordId);
AtomicBoolean first = new AtomicBoolean(true);
//已存在单条数据各个字段数据
Map<String, Map<String, Object>> fieldMaps = metaDataTables.stream().collect(Collectors.toMap(field -> (String) field.get("fieldId"), m -> m));
//遍历视图字段
viewFieldRelationVos.forEach(vf -> {
//数据
Map<String, Object> m = new HashMap<>();
if (Objects.nonNull(fieldMaps.get(vf.getFieldId()))) {
m.putAll(fieldMaps.get(vf.getFieldId()));
}
//获取分组
if (Objects.equals(vf.getFieldId(), MultiTableConstant.PHASE_FIELD_ID)) {
JSONArray.parseArray((String) m.get("value"), String.class).forEach(phaseId -> {
String id = phaseId.split("&&")[0];
String channelId = phaseId.split("&&")[1];
if (Objects.equals(channelId, dynamicView.getChannelId())) {
recordVo.setPhaseId(id);
}
});
}
//构建字段
MetaDataTableVo value = new MetaDataTableVo();
value.setFieldId(vf.getFieldId());
value.setFieldLength(vf.getFieldLength());
value.setRecordId(recordId);
value.setFieldType(vf.getFieldType());
//字段类型匹配设置
switch (vf.getFieldType()) {
//数字
case 2:
if (CollectionUtils.isNotEmpty(conditionMaps.get(vf.getFieldId()))) {
conditionMaps.get(vf.getFieldId()).forEach(condition -> {
conditionSet.add(checkComparion(condition, StringUtils.isNotEmpty((String) m.get("value")) ? (String) m.get("value") : StringUtils.EMPTY, 2));
conditionCount.decrementAndGet();
});
}
if (StringUtils.isNotEmpty((String) m.get("value"))) {
//数字格式化
DecimalFormat df = new DecimalFormat();
df.applyPattern(vf.getProperty().getFormatter().getFormat());
value.setValue(df.format(Double.valueOf((String) m.get("value"))));
value.setCompareValue((String) m.get("value"));
}
break;
//单选
case 3:
if (CollectionUtils.isNotEmpty(conditionMaps.get(vf.getFieldId()))) {
conditionMaps.get(vf.getFieldId()).forEach(condition -> {
conditionSet.add(checkComparion(condition, Set.of(StringUtils.isNotEmpty((String) m.get("value")) ? (String) m.get("value") : StringUtils.EMPTY), 3));
conditionCount.decrementAndGet();
});
}
if (StringUtils.isNotEmpty((String) m.get("value"))) {
value.setValue(m.get("value"));
if (Objects.equals(vf.getFieldId(), MultiTableConstant.FINISH_STATUS)) {
recordVo.setState(Integer.parseInt((String) m.get("value")));
}
if (Objects.equals(vf.getFieldId(), MultiTableConstant.RECORD_STATE)) {
recordVo.setRecordState(Integer.parseInt((String) m.get("value")));
}
if (sortMaps.containsKey(vf.getFieldId()) && Objects.nonNull(vf.getProperty()) && Objects.nonNull(vf.getProperty().getOptions())) {
Map<String, Option> options = vf.getProperty().getOptions().stream().collect(Collectors.toMap(Option::getId, o -> o));
Option option = options.get((String) m.get("value"));
value.setCompareValue(Objects.nonNull(option) ? option.getValue() : StringUtils.EMPTY);
}
}
break;
//多选
case 4:
if (CollectionUtils.isNotEmpty(conditionMaps.get(vf.getFieldId()))) {
conditionMaps.get(vf.getFieldId()).forEach(condition -> {
conditionSet.add(checkComparion(condition, Set.copyOf(JSONArray.parseArray(StringUtils.isNotEmpty((String) m.get("value")) ? (String) m.get("value") : "[]", String.class)), 4));
conditionCount.decrementAndGet();
});
}
if (StringUtils.isNotEmpty((String) m.get("value"))) {
List<String> ids = JSONArray.parseArray((String) m.get("value"), String.class);
value.setValue(ids);
if (sortMaps.containsKey(vf.getFieldId()) && Objects.nonNull(vf.getProperty()) && Objects.nonNull(vf.getProperty().getOptions())) {
Map<String, Option> options = vf.getProperty().getOptions().stream().collect(Collectors.toMap(Option::getId, o -> o));
StringBuilder sb = new StringBuilder();
ids.forEach(id -> {
if (options.containsKey(id)) {
sb.append(options.get(id));
}
});
value.setCompareValue(sb.toString());
}
}
break;
//日期
case 5:
if (CollectionUtils.isNotEmpty(conditionMaps.get(vf.getFieldId()))) {
conditionMaps.get(vf.getFieldId()).forEach(condition -> {
conditionSet.add(checkComparion(condition, Set.of(StringUtils.isNotEmpty((String) m.get("value")) ? (String) m.get("value") : "0"), 5));
conditionCount.decrementAndGet();
});
}
if (StringUtils.isNotEmpty((String) m.get("value"))) {
value.setValue(m.get("value"));
value.setCompareValue((String) m.get("value"));
} else {
value.setValue("0");
}
break;
//人员
case 11:
if (CollectionUtils.isNotEmpty(conditionMaps.get(vf.getFieldId()))) {
conditionMaps.get(vf.getFieldId()).forEach(condition -> {
conditionSet.add(checkComparion(condition, Set.copyOf(JSONArray.parseArray(StringUtils.isNotEmpty((String) m.get("value")) ? (String) m.get("value") : "[]")), 11));
conditionCount.decrementAndGet();
});
}
if (StringUtils.isNotEmpty((String) m.get("value"))) {
List<UserVO> userVOList = new ArrayList<>();
List<String> userIds = JSONArray.parseArray((String) m.get("value"), String.class);
StringBuilder sb = new StringBuilder();
userIds.forEach(userId -> {
if (userVOMaps.containsKey(userId)) {
userVOList.add(userVOMaps.get(userId));
sb.append(userVOMaps.get(userId).getNickName());
}
});
value.setValue(userVOList);
value.setCompareValue(sb.toString());
}
break;
//超链接
case 15:
if (CollectionUtils.isNotEmpty(conditionMaps.get(vf.getFieldId()))) {
String linkValue = "";
if (StringUtils.isNotEmpty((String) m.get("value"))) {
linkValue = JSONObject.parseObject((String) m.get("value"), HyperlinkEntity.class).getLinkValue();
}
String finalLinkValue = linkValue;
conditionMaps.get(vf.getFieldId()).forEach(condition -> {
conditionSet.add(checkComparion(condition, Set.of(finalLinkValue), 15));
conditionCount.decrementAndGet();
});
}
if (StringUtils.isNotEmpty((String) m.get("value"))) {
HyperlinkEntity hyperlink = JSONObject.parseObject((String) m.get("value"), HyperlinkEntity.class);
value.setValue(hyperlink);
value.setCompareValue(hyperlink.getLinkValue());
}
break;
//附件
case 17:
if (StringUtils.isNotEmpty((String) m.get("value"))) {
if (spaceFileMaps.containsKey((String) m.get("value"))) {
List<DemandFileExtendBO> bos = spaceFileMaps.get((String) m.get("value"));
value.setValue(bos);
StringBuilder sb = new StringBuilder();
if (sortMaps.containsKey(vf.getFieldId()) && CollectionUtils.isNotEmpty(bos)) {
bos.forEach(fileExtendBO -> {
sb.append(fileExtendBO.getName());
});
}
value.setCompareValue(sb.toString());
}
}
if (CollectionUtils.isNotEmpty(conditionMaps.get(vf.getFieldId()))) {
conditionMaps.get(vf.getFieldId()).forEach(condition -> {
conditionSet.add(checkComparion(condition, value.getCompareValue(), 17));
conditionCount.decrementAndGet();
});
}
break;
default:
value.setCompareValue((String) m.get("value"));
value.setValue(m.get("value"));
if (CollectionUtils.isNotEmpty(conditionMaps.get(vf.getFieldId()))) {
conditionMaps.get(vf.getFieldId()).forEach(condition -> {
conditionSet.add(checkComparion(condition, Set.of(Optional.ofNullable(m.get("value")).orElse(StringUtils.EMPTY)), 1));
conditionCount.decrementAndGet();
});
}
}
metaDataTableVos.add(value);
});
if (Objects.equals(dynamicView.getRule(), RuleEnum.ALL) && Objects.equals(conditionCount.get(), 0) && !conditionSet.contains(false)) {
recordVo.setData(metaDataTableVos);
recordVoList.add(recordVo);
} else if (Objects.equals(dynamicView.getRule(), RuleEnum.ANY) && conditionCount.get() < conditions.size() && conditionSet.contains(true)) {
recordVo.setData(metaDataTableVos);
recordVoList.add(recordVo);
}
});
//排序
Collections.sort(recordVoList, new RecordCompareVo(dynamicView.getSortList()));
}
return recordVoList;
}
筛选处理方法
/**
* 筛选匹配
*
* @param condition
* @param value
* @param fieldType
* @param <T>
* @return
*/
private <T> Boolean checkComparion(DynamicCondition condition, T value, int fieldType) {
Set<String> matchValue = condition.getMatchValue();
AtomicBoolean result = new AtomicBoolean(true);
if (Objects.isNull(condition)) {
return result.get();
}
switch (condition.getMatchRule()) {
//相等
case EQUAL:
if ((value instanceof Set) && CollectionUtils.isNotEmpty(matchValue)) {
result.set(matchValue.containsAll((Set) value) && matchValue.size() == ((Set) value).size());
}
if ((value instanceof String) && CollectionUtils.isNotEmpty(matchValue)) {
matchValue.forEach(mv -> {
//时间
if (Objects.equals(fieldType, 5)) {
Long d1 = Math.abs(Long.valueOf(mv));
Long d2 = Math.abs(Long.valueOf((String) value));
result.set((d2 - d1) == 0);
} else if (Objects.equals(fieldType, 2)) {
if (StringUtils.isNotEmpty((String) value)) {
result.set((Double.valueOf((String) value)).compareTo((Double.valueOf(mv))) == 0);
} else {
result.set(false);
}
}
});
}
break;
//不等于
case NOT_EQUAL:
if ((value instanceof Set) && CollectionUtils.isNotEmpty(matchValue)) {
result.set(!((Set<?>) value).containsAll(matchValue));
}
if ((value instanceof String) && CollectionUtils.isNotEmpty(matchValue)) {
matchValue.forEach(mv -> {
//时间
if (Objects.equals(fieldType, 5)) {
Long d1 = Math.abs(Long.valueOf(mv));
Long d2 = Math.abs(Long.valueOf((String) value));
result.set((d2 - d1) != 0);
}
//数字
if (Objects.equals(fieldType, 2)) {
if (StringUtils.isNotEmpty((String) value)) {
result.set((Double.valueOf((String) value)).compareTo((Double.valueOf(mv))) != 0);
} else {
result.set(((String) value).compareTo(mv) != 0);
}
}
});
}
break;
//大于
case THAN:
if ((value instanceof String) && CollectionUtils.isNotEmpty(matchValue)) {
matchValue.forEach(mv -> {
//时间
if (Objects.equals(fieldType, 5)) {
Long d1 = Math.abs(Long.valueOf(mv));
Long d2 = Math.abs(Long.valueOf((String) value));
result.set((d2 - d1) > 0);
}
//数字
if (Objects.equals(fieldType, 2)) {
if (StringUtils.isNotEmpty((String) value)) {
result.set((Double.valueOf((String) value)).compareTo((Double.valueOf(mv))) > 0);
} else {
result.set(false);
}
}
});
}
break;
//大于等于
case THAN_EQUAL:
if ((value instanceof String) && CollectionUtils.isNotEmpty(matchValue)) {
matchValue.forEach(mv -> {
if (StringUtils.isNotEmpty((String) value)) {
result.set((Double.valueOf((String) value)).compareTo((Double.valueOf(mv))) >= 0);
} else {
result.set(false);
}
});
}
break;
//小于
case LESS:
if ((value instanceof String) && CollectionUtils.isNotEmpty(matchValue)) {
matchValue.forEach(mv -> {
//时间
if (Objects.equals(fieldType, 5)) {
Long d1 = Math.abs(Long.valueOf(mv));
Long d2 = Math.abs(Long.valueOf((String) value));
result.set((d2 - d1) < 0);
}
//数字
if (Objects.equals(fieldType, 2)) {
if (StringUtils.isNotEmpty((String) value)) {
result.set((Double.valueOf((String) value)).compareTo((Double.valueOf(mv))) < 0);
} else {
result.set(false);
}
}
});
}
break;
//小于等于
case LESS_EQUAL:
if ((value instanceof String) && CollectionUtils.isNotEmpty(matchValue)) {
matchValue.forEach(mv -> {
//数字
if (StringUtils.isNotEmpty((String) value)) {
result.set((Double.valueOf((String) value)).compareTo((Double.valueOf(mv))) <= 0);
} else {
result.set(false);
}
});
}
break;
//包含
case CONTAIN:
if ((value instanceof Set) && CollectionUtils.isNotEmpty(matchValue)) {
AtomicBoolean skip = new AtomicBoolean(false);
matchValue.forEach(mv -> {
if (!skip.get()) {
skip.set(((Set<?>) value).contains(mv));
}
});
result.set(skip.get());
}
break;
//不包含
case NOT_CONTAIN:
if ((value instanceof Set) && CollectionUtils.isNotEmpty(matchValue)) {
AtomicBoolean skip = new AtomicBoolean(false);
matchValue.forEach(mv -> {
if (!skip.get()) {
skip.set(!((Set<?>) value).contains(mv));
}
});
result.set(skip.get());
}
break;
//为空
case EMPTY:
//时间
if (Objects.equals(fieldType, 5)) {
result.set(Long.valueOf((String) value) == 0);
} else if (Objects.nonNull(value)) {
if ((value instanceof String) && StringUtils.isNotEmpty((String) value)) {
result.set(false);
} else {
result.set(false);
}
}
break;
//不为空
case NOT_EMPTY:
//时间
if (Objects.equals(fieldType, 5)) {
result.set(Long.valueOf((String) value) != 0);
} else if (Objects.isNull(value)) {
result.set(false);
}
break;
default:
}
return result.get();
}