using System.Text.Json; using Workflow.Domain.Engine; using Workflow.Domain.Entities; namespace Workflow.Application.Engine; /// /// WorkflowGraph(X6 JSON 强类型)与 WorkflowNode/WorkflowEdge(领域实体)的双向适配层。 /// 使 ProcessEngine 从「读关系表」切换到「读 X6 JSON 快照」时,下游所有 Process*Async 方法零改动—— /// 引擎启动时把图映射成内存实体集合,FindNodeAsync/GetOutgoingEdges 等改查内存集合即可。 /// /// ID 兼容约定(见技术方案文档 5.7):X6 节点/边 id 用纯 Guid 字符串(前端 crypto.randomUUID() 生成), /// 不带 "node_" 前缀,保证 Guid ↔ string 无损往返(WorkflowToken.NodeId 是 Guid)。 /// public static class WorkflowGraphAdapter { private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web); /// 把 WorkflowGraph 映射成内存 WorkflowNode 集合(供 ProcessEngine 查询)。 public static List ToNodes(WorkflowGraph graph, Guid definitionId) { var nodes = new List(graph.Nodes.Count); foreach (var gn in graph.Nodes) { var nodeType = WorkflowShapeMap.ToNodeType(gn.Shape); if (nodeType is null) continue; // 未知 shape 跳过(发布校验已拦截) nodes.Add(new WorkflowNode { Id = ParseGuid(gn.Id), DefinitionId = definitionId, NodeType = nodeType.Value, Name = gn.GetTitle() ?? nodeType.Value.ToString(), // Config:把 data 对象序列化回字符串,复用现有 NodeConfigParser 解析链 Config = gn.Data is { ValueKind: JsonValueKind.Object or JsonValueKind.Array } data ? data.GetRawText() : null, // 审批节点绑定的表单定义 id(钉钉式树编译进 data.formDefinitionId)。 // ApproveTaskCommandHandler.SaveNodeFormDataAsync 据此加载表单做字段权限校验。 FormDefinitionId = TryGetFormDefinitionId(gn.Data), PositionX = (int)Math.Round(gn.X), PositionY = (int)Math.Round(gn.Y), // 审计字段填充非空默认值(实体是 IFullAudit,避免下游 null 引用) CreatedBy = "system", UpdatedBy = "system", }); } return nodes; } /// 把 WorkflowGraph 映射成内存 WorkflowEdge 集合(供 ProcessEngine 查询)。 public static List ToEdges(WorkflowGraph graph, Guid definitionId) { var edges = new List(graph.Edges.Count); foreach (var ge in graph.Edges) { var sourceId = ParseGuid(ge.Source.Cell); var targetId = ParseGuid(ge.Target.Cell); if (sourceId == Guid.Empty || targetId == Guid.Empty) continue; edges.Add(new WorkflowEdge { Id = ParseGuid(ge.Id), DefinitionId = definitionId, SourceNodeId = sourceId, TargetNodeId = targetId, EdgeType = ge.GetEdgeType(), Label = null, // 引擎不读 Label Condition = ge.GetCondition(), Order = ge.GetOrder(), CreatedBy = "system", UpdatedBy = "system", }); } return edges; } /// 纯 Guid 字符串解析为 Guid;非法返回 Guid.Empty(适配层会跳过)。 private static Guid ParseGuid(string? id) { if (string.IsNullOrWhiteSpace(id)) return Guid.Empty; return Guid.TryParse(id, out var g) ? g : Guid.Empty; } /// 从节点 data.formDefinitionId(钉钉式树编译产物)提取表单定义 id。 private static Guid? TryGetFormDefinitionId(System.Text.Json.JsonElement? data) { if (data is not { ValueKind: System.Text.Json.JsonValueKind.Object } obj) return null; if (!obj.TryGetProperty("formDefinitionId", out var prop)) return null; var raw = prop.ValueKind == System.Text.Json.JsonValueKind.String ? prop.GetString() : null; return Guid.TryParse(raw, out var g) ? g : null; } }