Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@
import static modelengine.fitframework.util.ObjectUtils.cast;
import static modelengine.fitframework.util.StringUtils.lengthBetween;

import modelengine.fit.jober.aipp.common.exception.AippParamException;
import modelengine.fit.jober.aipp.enums.InputParamType;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import modelengine.fitframework.inspection.Validation;
import modelengine.fitframework.util.MapBuilder;
import modelengine.fit.jober.aipp.common.exception.AippParamException;
import modelengine.fitframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

Expand All @@ -32,76 +31,257 @@
* @since 2024-11-25
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AppInputParam {
private int stringMaxLength = 500;
private Map<InputParamType, Predicate<Object>> paramTypePredicateMap;
private static final int DEFAULT_STRING_MAX_LENGTH = 500;
private static final BigDecimal DEFAULT_MIN_NUMBER = new BigDecimal("-999999999.99");
private static final BigDecimal DEFAULT_MAX_NUMBER = new BigDecimal("999999999.99");
private static final int MAX_DECIMAL_PLACES = 2;

private String name;
private String type;
private String description;
private String displayName;
private boolean isRequired;
private boolean isVisible;
private Integer stringMaxLength;
private Predicate<Object> validator;

/**
* 通过键值对构建一个 {@link AppInputParam} 对象.
* 通过键值对构建一个 {@link AppInputParam} 对象
*
* @param rawParam 原始参数.
* @return {@link AppInputParam} 对象.
* @param rawParam 原始参数
* @return {@link AppInputParam} 对象
*/
public static AppInputParam from(Map<String, Object> rawParam) {
AppInputParam appInputParam = new AppInputParam();
appInputParam.setName(ObjectUtils.cast(rawParam.get("name")));
appInputParam.setType(ObjectUtils.cast(rawParam.get("type")));
appInputParam.setDescription(ObjectUtils.cast(rawParam.get("description")));
appInputParam.setRequired(ObjectUtils.cast(rawParam.getOrDefault("isRequired", true)));
appInputParam.setVisible(ObjectUtils.cast(rawParam.getOrDefault("isVisible", true)));
Integer stringMaxLength = cast(rawParam.getOrDefault("stringMaxLength", 500));
appInputParam.setStringMaxLength(stringMaxLength);
Map<InputParamType, Predicate<Object>> paramTypePredicateMap
= MapBuilder.<InputParamType, Predicate<Object>>get()
.put(InputParamType.STRING_TYPE,
v -> v instanceof String && lengthBetween(cast(v), 1, stringMaxLength, true, true))
.put(InputParamType.BOOLEAN_TYPE, v -> v instanceof Boolean)
.put(InputParamType.INTEGER_TYPE,
v -> v instanceof Integer && ObjectUtils.between((int) v, -999999999, 999999999))
.put(InputParamType.NUMBER_TYPE, AppInputParam::isValidNumber)
if (rawParam == null) {
throw new IllegalArgumentException("rawParam cannot be null");
}

AppearanceConfig appearance = AppearanceConfig.from(rawParam);
String type = cast(rawParam.get("type"));

return AppInputParam.builder()
.name(cast(rawParam.get("name")))
.type(type)
.displayName(cast(rawParam.get("displayName")))
.isRequired(cast(rawParam.getOrDefault("isRequired", true)))
.isVisible(cast(rawParam.getOrDefault("isVisible", true)))
.stringMaxLength(appearance.getStringMaxLength())
.validator(createValidator(appearance, type))
.build();
appInputParam.setParamTypePredicateMap(paramTypePredicateMap);
return appInputParam;
}

private static boolean isValidNumber(Object value) {
/**
* 校验数据
*
* @param dataMap 数据集合
* @throws AippParamException 当参数无效时抛出
*/
public void validate(Map<String, Object> dataMap) {
if (dataMap == null) {
throw new IllegalArgumentException("dataMap cannot be null");
}

Object value = dataMap.get(this.name);

// 校验必填项
if (this.isRequired && value == null) {
throw new AippParamException(INPUT_PARAM_IS_INVALID, this.displayName);
}

// 如果值为null且非必填,则跳过后续校验
if (value == null) {
return;
}

// 使用预构建的校验器
if (this.validator != null && !this.validator.test(value)) {
throw new AippParamException(INPUT_PARAM_IS_INVALID, this.displayName);
}
}

/**
* 根据外观配置创建校验器
*/
private static Predicate<Object> createValidator(AppearanceConfig config, String type) {
String displayType = config.getDisplayType();

if (displayType == null) {
return createDefaultValidator(type);
}

return switch (displayType) {
case "input" -> createStringValidator(config.getStringMaxLength());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image 这块和前端组件有比较强的相关性,可以考虑是否基于当前成熟的UI组件类型对应。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个displaytype与前端定义的displaytype对应,可以考虑优化名称

case "number" -> createNumberValidator(config.getMinValue(), config.getMaxValue());
case "dropdown" -> createDropdownValidator(config.getOptions());
case "switch" -> createBooleanValidator();
case "multiselect" -> createArrayValidator(config.getOptions());
default -> createDefaultValidator(type);
};
}

private static Predicate<Object> createStringValidator(Integer maxLength) {
return value -> {
if (!(value instanceof String str)) {
return false;
}
// 如果没有设置 maxLength,则不限制长度
return maxLength == null || lengthBetween(str, 0, maxLength, true, true);
};
}

private static Predicate<Object> createNumberValidator(BigDecimal minValue, BigDecimal maxValue) {
return value -> {
if (!(value instanceof Number)) {
return false;
}

try {
BigDecimal numberValue = new BigDecimal(value.toString());

if (minValue != null && numberValue.compareTo(minValue) < 0) {
return false;
}

if (maxValue != null && numberValue.compareTo(maxValue) > 0) {
return false;
}

return true;
} catch (NumberFormatException e) {
return false;
}
};
}

private static Predicate<Object> createDropdownValidator(List<String> options) {
return value -> options != null &&
!options.isEmpty() &&
options.contains(String.valueOf(value));
}

private static Predicate<Object> createArrayValidator(List<String> options) {
return value -> {
if (options == null || options.isEmpty()) {
return false;
}

if (!(value instanceof Collection<?> valueCollection)) {
return false;
}

return valueCollection.stream()
.map(String::valueOf)
.allMatch(options::contains);
};
}

private static Predicate<Object> createBooleanValidator() {
return value -> value instanceof Boolean;
}

private static Predicate<Object> createDefaultValidator(String type) {
if (type == null) {
return value -> false; // 如果类型未定义,则校验失败
}

return switch (type.toLowerCase()) {
case "string" -> value -> {
if (!(value instanceof String str)) {
return false;
}
return lengthBetween(str, 1, DEFAULT_STRING_MAX_LENGTH, true, true);
};

case "integer"-> value -> {
if (value instanceof Integer) {
return ObjectUtils.between((int) value, -999999999, 999999999);
}
return false;
};

case "number" -> value -> {
if (value instanceof Number) {
return isValidDecimalNumber(value);
}
return false;
};

case "boolean" -> value -> value instanceof Boolean;

default -> value -> true;
};
}

private static boolean isValidDecimalNumber(Object value) {
if (!(value instanceof Number)) {
return false;
}
BigDecimal numberValue = new BigDecimal(value.toString());
if (numberValue.compareTo(new BigDecimal("-999999999.99")) < 0
|| numberValue.compareTo(new BigDecimal("999999999.99")) > 0) {

try {
BigDecimal numberValue = new BigDecimal(value.toString());

// 检查数值范围
if (numberValue.compareTo(DEFAULT_MIN_NUMBER) < 0 ||
numberValue.compareTo(DEFAULT_MAX_NUMBER) > 0) {
return false;
}

// 检查小数位数
return numberValue.scale() <= MAX_DECIMAL_PLACES;
} catch (NumberFormatException e) {
return false;
}
int scale = numberValue.scale();
return scale <= 2;
}

/**
* 校验数据.
*
* @param dataMap 数据集合.
* 外观配置内部类,用于封装外观相关参数
*/
public void validate(Map<String, Object> dataMap) {
String paramName = this.getName();
if (this.isRequired()) {
Validation.notNull(cast(dataMap.get(paramName)),
() -> new AippParamException(INPUT_PARAM_IS_INVALID, paramName));
@Getter
private static class AppearanceConfig {
private final String displayType;
private final Integer stringMaxLength;
private final BigDecimal minValue;
private final BigDecimal maxValue;
private final List<String> options;

private AppearanceConfig(String displayType, Integer stringMaxLength,
BigDecimal minValue, BigDecimal maxValue, List<String> options) {
this.displayType = displayType;
this.stringMaxLength = stringMaxLength;
this.minValue = minValue;
this.maxValue = maxValue;
this.options = options;
}
if (dataMap.get(paramName) == null) {
return;

public static AppearanceConfig from(Map<String, Object> rawParam) {
Map<String, Object> appearance = cast(rawParam.get("appearance"));

if (appearance == null) {
return new AppearanceConfig(null, null, null, null, null);
}

String displayType = cast(appearance.get("displayType"));
Integer stringMaxLength = cast(appearance.get("maxLength"));

BigDecimal minValue = parseNumber(appearance.get("minValue"));
BigDecimal maxValue = parseNumber(appearance.get("maxValue"));
List<String> options = cast(appearance.get("options"));

return new AppearanceConfig(displayType, stringMaxLength, minValue, maxValue, options);
}

Object v = dataMap.get(this.getName());
Predicate<Object> predicate = paramTypePredicateMap.get(InputParamType.getParamType(this.getType()));
if (!predicate.test(v)) {
throw new AippParamException(INPUT_PARAM_IS_INVALID, paramName);
private static BigDecimal parseNumber(Object value) {
if (value == null) {
return null;
}
try {
return new BigDecimal(value.toString());
} catch (NumberFormatException e) {
return null;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static List<AppInputParam> getAppInputParams(FlowsService service, String
AppInputParam param = new AppInputParam();
param.setName(ObjectUtils.cast(rawParam.get("name")));
param.setType(ObjectUtils.cast(rawParam.get("type")));
param.setDescription(ObjectUtils.cast(rawParam.get("description")));
param.setDisplayName(ObjectUtils.cast(rawParam.get("displayName")));
param.setRequired(ObjectUtils.cast(rawParam.getOrDefault("isRequired", true)));
param.setVisible(ObjectUtils.cast(rawParam.getOrDefault("isVisible", true)));
return param;
Expand Down
Loading