- 将监控模块表前缀从 mon_ 改为 b_ - 为工作流审批任务处理器添加流程引擎图加载逻辑 - 移除外部系统相关DTO和查询类的直接引用改为查询对象模式 - 为脚本执行服务添加模块程序集诊断日志 - 为分享表单提交功能增加密码验证支持 - 集成脚本模块数据库迁移支持 - 更新通知服务测试以兼容IOptionsMonitor接口变化
94 lines
4.3 KiB
C#
94 lines
4.3 KiB
C#
using System.Text.Json;
|
||
using Workflow.Domain.Engine;
|
||
using Workflow.Domain.Entities;
|
||
|
||
namespace Workflow.Application.Engine;
|
||
|
||
/// <summary>
|
||
/// 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)。
|
||
/// </summary>
|
||
public static class WorkflowGraphAdapter
|
||
{
|
||
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
|
||
|
||
/// <summary>把 WorkflowGraph 映射成内存 WorkflowNode 集合(供 ProcessEngine 查询)。</summary>
|
||
public static List<WorkflowNode> ToNodes(WorkflowGraph graph, Guid definitionId)
|
||
{
|
||
var nodes = new List<WorkflowNode>(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;
|
||
}
|
||
|
||
/// <summary>把 WorkflowGraph 映射成内存 WorkflowEdge 集合(供 ProcessEngine 查询)。</summary>
|
||
public static List<WorkflowEdge> ToEdges(WorkflowGraph graph, Guid definitionId)
|
||
{
|
||
var edges = new List<WorkflowEdge>(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;
|
||
}
|
||
|
||
/// <summary>纯 Guid 字符串解析为 Guid;非法返回 Guid.Empty(适配层会跳过)。</summary>
|
||
private static Guid ParseGuid(string? id)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(id)) return Guid.Empty;
|
||
return Guid.TryParse(id, out var g) ? g : Guid.Empty;
|
||
}
|
||
|
||
/// <summary>从节点 data.formDefinitionId(钉钉式树编译产物)提取表单定义 id。</summary>
|
||
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;
|
||
}
|
||
}
|