This commit is contained in:
2025-08-27 19:58:49 +08:00
parent 12eb05ddf3
commit 489a15acad

View File

@@ -0,0 +1,213 @@
/*
* Copyright (c) 2023-2025, Agents-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.agentsflex.core.chain;
import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
/**
* 表示一个链式节点校验结果。
* 包含校验是否成功、消息说明以及附加的详细信息(如失败字段、原因等)。
* <p>
* 实例是不可变的immutable线程安全。
*/
public class ChainNodeValidResult implements Serializable {
private static final long serialVersionUID = 1L;
public static final ChainNodeValidResult SUCCESS = new ChainNodeValidResult(true, null, null);
public static final ChainNodeValidResult FAILURE = new ChainNodeValidResult(false, null, null);
private final boolean success;
private final String message;
private final Map<String, Object> details;
/**
* 私有构造器,确保通过工厂方法创建实例。
*/
private ChainNodeValidResult(boolean success, String message, Map<String, Object> details) {
this.success = success;
this.message = message;
// 防御性拷贝,防止外部修改
this.details = details != null ? Collections.unmodifiableMap(new java.util.HashMap<>(details)) : null;
}
/**
* 获取校验是否成功。
*/
public boolean isSuccess() {
return success;
}
/**
* 获取结果消息(可为 null
*/
public String getMessage() {
return message;
}
/**
* 获取详细信息(如校验失败的字段、原因等),不可变 Map。
* 如果无详情,则返回 null。
*/
public Map<String, Object> getDetails() {
return details;
}
// ------------------ 静态工厂方法 ------------------
/**
* 创建一个成功的校验结果(无消息、无详情)。
*/
public static ChainNodeValidResult ok() {
return SUCCESS;
}
/**
* 创建一个成功的校验结果,附带消息。
*/
public static ChainNodeValidResult ok(String message) {
return new ChainNodeValidResult(true, message, null);
}
/**
* 创建一个成功的校验结果,附带消息和详情。
*/
public static ChainNodeValidResult ok(String message, Map<String, Object> details) {
return new ChainNodeValidResult(true, message, details);
}
/**
* 创建一个成功的校验结果,支持键值对形式传入 details。
* <p>
* 示例success("验证通过", "userId", 123, "role", "admin")
*
* @param message 消息
* @param kvPairs 键值对必须成对key1, value1, key2, value2...
* @return ChainNodeValidResult
* @throws IllegalArgumentException 如果 kvPairs 数量为奇数
*/
public static ChainNodeValidResult ok(String message, Object... kvPairs) {
Map<String, Object> details = toMapFromPairs(kvPairs);
return new ChainNodeValidResult(true, message, details);
}
/**
* 创建一个失败的校验结果(无消息、无详情)。
*/
public static ChainNodeValidResult fail() {
return FAILURE;
}
/**
* 创建一个失败的校验结果,仅包含消息。
*/
public static ChainNodeValidResult fail(String message) {
return new ChainNodeValidResult(false, message, null);
}
/**
* 创建一个失败的校验结果,包含消息和详情。
*/
public static ChainNodeValidResult fail(String message, Map<String, Object> details) {
return new ChainNodeValidResult(false, message, details);
}
/**
* 创建一个失败的校验结果,支持键值对形式传入 details。
* <p>
* 示例fail("验证失败", "field", "email", "reason", "格式错误")
*/
public static ChainNodeValidResult fail(String message, Object... kvPairs) {
Map<String, Object> details = toMapFromPairs(kvPairs);
return new ChainNodeValidResult(false, message, details);
}
/**
* 快捷方法:创建包含字段错误的失败结果。
* 适用于表单/参数校验场景。
*
* @param field 错误字段名
* @param reason 错误原因
* @return 失败结果
*/
public static ChainNodeValidResult failOnField(String field, String reason) {
Map<String, Object> details = Collections.singletonMap("fieldError", field + ": " + reason);
return fail(reason, details);
}
/**
* 快捷方法:基于布尔值返回成功或失败结果。
*
* @param condition 条件
* @param messageIfFail 条件不满足时的消息
* @return 根据条件返回对应结果
*/
public static ChainNodeValidResult require(boolean condition, String messageIfFail) {
return condition ? ok() : fail(messageIfFail);
}
private static Map<String, Object> toMapFromPairs(Object... kvPairs) {
if (kvPairs == null || kvPairs.length == 0) {
return null;
}
if (kvPairs.length % 2 != 0) {
throw new IllegalArgumentException("kvPairs must be even-sized: key1, value1, key2, value2...");
}
Map<String, Object> map = new java.util.HashMap<>();
for (int i = 0; i < kvPairs.length; i += 2) {
Object key = kvPairs[i];
Object value = kvPairs[i + 1];
if (!(key instanceof String)) {
throw new IllegalArgumentException("Key must be a String, but got: " + key);
}
map.put((String) key, value);
}
return Collections.unmodifiableMap(map);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ChainNodeValidResult)) return false;
ChainNodeValidResult that = (ChainNodeValidResult) o;
return success == that.success &&
Objects.equals(message, that.message) &&
Objects.equals(details, that.details);
}
@Override
public int hashCode() {
return Objects.hash(success, message, details);
}
@Override
public String toString() {
return "ChainNodeValidResult{" +
"success=" + success +
", message='" + message + '\'' +
", details=" + details +
'}';
}
}