* feat(ui): 完善 UI 布局系统和编辑器可视化工具 * refactor: 移除 ModuleRegistry,统一使用 PluginManager 插件系统 * fix: 修复 CodeQL 警告并提升测试覆盖率 * refactor: 分离运行时入口点,解决 runtime bundle 包含 React 的问题 * fix(ci): 添加 editor-core 和 editor-runtime 到 CI 依赖构建步骤 * docs: 完善 ServiceContainer 文档,新增 Symbol.for 模式和 @InjectProperty 说明 * fix(ci): 修复 type-check 失败问题 * fix(ci): 修复类型检查失败问题 * fix(ci): 修复类型检查失败问题 * fix(ci): behavior-tree 构建添加 @tauri-apps 外部依赖 * fix(ci): behavior-tree 添加 @tauri-apps/plugin-fs 类型依赖 * fix(ci): platform-web 添加缺失的 behavior-tree 依赖 * fix(lint): 移除正则表达式中不必要的转义字符
191 lines
4.6 KiB
TypeScript
191 lines
4.6 KiB
TypeScript
import { NodeTemplate } from '../../..';
|
||
import { Position, NodeType } from '../value-objects';
|
||
import { ValidationError } from '../errors';
|
||
|
||
/**
|
||
* 行为树节点领域实体
|
||
* 封装节点的业务逻辑和验证规则
|
||
*/
|
||
export class Node {
|
||
private readonly _id: string;
|
||
private readonly _template: NodeTemplate;
|
||
private _data: Record<string, unknown>;
|
||
private _position: Position;
|
||
private _children: string[];
|
||
private readonly _nodeType: NodeType;
|
||
|
||
constructor(
|
||
id: string,
|
||
template: NodeTemplate,
|
||
data: Record<string, unknown>,
|
||
position: Position,
|
||
children: string[] = []
|
||
) {
|
||
this._id = id;
|
||
this._template = template;
|
||
this._data = { ...data };
|
||
this._position = position;
|
||
this._children = [...children];
|
||
this._nodeType = NodeType.fromString(template.type);
|
||
}
|
||
|
||
get id(): string {
|
||
return this._id;
|
||
}
|
||
|
||
get template(): NodeTemplate {
|
||
return this._template;
|
||
}
|
||
|
||
get data(): Record<string, unknown> {
|
||
return { ...this._data };
|
||
}
|
||
|
||
get position(): Position {
|
||
return this._position;
|
||
}
|
||
|
||
get children(): ReadonlyArray<string> {
|
||
return this._children;
|
||
}
|
||
|
||
get nodeType(): NodeType {
|
||
return this._nodeType;
|
||
}
|
||
|
||
/**
|
||
* 更新节点位置
|
||
*/
|
||
moveToPosition(newPosition: Position): Node {
|
||
return new Node(
|
||
this._id,
|
||
this._template,
|
||
this._data,
|
||
newPosition,
|
||
this._children
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 更新节点数据
|
||
*/
|
||
updateData(data: Record<string, unknown>): Node {
|
||
return new Node(
|
||
this._id,
|
||
this._template,
|
||
{ ...this._data, ...data },
|
||
this._position,
|
||
this._children
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 添加子节点
|
||
* @throws ValidationError 如果违反业务规则
|
||
*/
|
||
addChild(childId: string): Node {
|
||
// 使用模板定义的约束,undefined 表示无限制
|
||
const maxChildren = (this._template.maxChildren ?? Infinity) as number;
|
||
|
||
if (maxChildren === 0) {
|
||
throw ValidationError.leafNodeNoChildren();
|
||
}
|
||
|
||
if (this._children.length >= maxChildren) {
|
||
if (this._nodeType.isRoot()) {
|
||
throw ValidationError.rootNodeMaxChildren();
|
||
}
|
||
if (this._nodeType.isDecorator()) {
|
||
throw ValidationError.decoratorNodeMaxChildren();
|
||
}
|
||
throw new ValidationError(`节点 ${this._id} 已达到最大子节点数 ${maxChildren}`);
|
||
}
|
||
|
||
if (this._children.includes(childId)) {
|
||
throw new ValidationError(`子节点 ${childId} 已存在`);
|
||
}
|
||
|
||
return new Node(
|
||
this._id,
|
||
this._template,
|
||
this._data,
|
||
this._position,
|
||
[...this._children, childId]
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 移除子节点
|
||
*/
|
||
removeChild(childId: string): Node {
|
||
return new Node(
|
||
this._id,
|
||
this._template,
|
||
this._data,
|
||
this._position,
|
||
this._children.filter((id) => id !== childId)
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 检查是否可以添加子节点
|
||
*/
|
||
canAddChild(): boolean {
|
||
// 使用模板定义的最大子节点数,undefined 表示无限制
|
||
const maxChildren = (this._template.maxChildren ?? Infinity) as number;
|
||
return this._children.length < maxChildren;
|
||
}
|
||
|
||
/**
|
||
* 检查是否有子节点
|
||
*/
|
||
hasChildren(): boolean {
|
||
return this._children.length > 0;
|
||
}
|
||
|
||
/**
|
||
* 检查是否为根节点
|
||
*/
|
||
isRoot(): boolean {
|
||
return this._nodeType.isRoot();
|
||
}
|
||
|
||
/**
|
||
* 转换为普通对象(用于序列化)
|
||
*/
|
||
toObject(): {
|
||
id: string;
|
||
template: NodeTemplate;
|
||
data: Record<string, unknown>;
|
||
position: { x: number; y: number };
|
||
children: string[];
|
||
} {
|
||
return {
|
||
id: this._id,
|
||
template: this._template,
|
||
data: this._data,
|
||
position: this._position.toObject(),
|
||
children: [...this._children]
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 从普通对象创建节点
|
||
*/
|
||
static fromObject(obj: {
|
||
id: string;
|
||
template: NodeTemplate;
|
||
data: Record<string, unknown>;
|
||
position: { x: number; y: number };
|
||
children: string[];
|
||
}): Node {
|
||
return new Node(
|
||
obj.id,
|
||
obj.template,
|
||
obj.data,
|
||
Position.fromObject(obj.position),
|
||
obj.children
|
||
);
|
||
}
|
||
}
|