fix(scripting): ScriptTypeRegistry 程序集过滤按真实模块前缀匹配

原实现按 'BuildFlow.' 前缀过滤,但本仓库业务程序集命名为
Platform.* / Workflow.* / Scripting.* / File.* / IM.* / Rag.* /
BuildFlow.*,仅 rag 与共享层用 BuildFlow 前缀。
导致 Scan() 结果恒为空、方法面板无任何类型可展示。

改为按业务模块前缀白名单匹配,并排除 .Host 程序集。

Task T4 of scripting module refactor
This commit is contained in:
xiangning 2026-07-07 09:11:02 +08:00
parent 989c28da4d
commit bfb83957b6

View File

@ -14,6 +14,16 @@ public class ScriptTypeRegistry
{ {
private readonly ConcurrentDictionary<string, ScriptTypeMeta> _types = new(); private readonly ConcurrentDictionary<string, ScriptTypeMeta> _types = new();
/// <summary>
/// 业务模块程序集前缀白名单。脚本编辑器应能调用的 CQRS 入口集中在各业务模块的 Application 层;
/// 命名约定为 <c>&lt;Module&gt;.Application/Domain/Infrastructure</c>,模块根名如下。
/// 注意:rag 模块与共享层使用 <c>BuildFlow.</c> 前缀,故 BuildFlow 也纳入。
/// </summary>
private static readonly string[] _modulePrefixes =
{
"Platform", "Workflow", "Scripting", "File", "IM", "Rag", "BuildFlow"
};
/// <summary>执行(或刷新)扫描。幂等:重复调用会重建字典。</summary> /// <summary>执行(或刷新)扫描。幂等:重复调用会重建字典。</summary>
public void Scan() public void Scan()
{ {
@ -21,11 +31,7 @@ public class ScriptTypeRegistry
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{ {
var name = asm.GetName().Name ?? string.Empty; var name = asm.GetName().Name ?? string.Empty;
if (!name.StartsWith("BuildFlow", StringComparison.Ordinal)) continue; if (!IsBusinessModuleAssembly(name)) continue;
// 排除测试与基础设施程序集:脚本沙箱内无法引用这些类型,列出它们只会误导用户
if (name.EndsWith(".Tests", StringComparison.Ordinal)) continue;
if (name.EndsWith(".Test", StringComparison.Ordinal)) continue;
if (name.EndsWith(".Infrastructure", StringComparison.Ordinal)) continue;
Type[] types; Type[] types;
try { types = asm.GetTypes(); } try { types = asm.GetTypes(); }
@ -47,6 +53,27 @@ public class ScriptTypeRegistry
foreach (var m in found) _types[m.FullName] = m; foreach (var m in found) _types[m.FullName] = m;
} }
/// <summary>
/// 判定程序集是否属于业务模块(而非框架/测试/宿主/基础设施)。
/// 脚本沙箱内无法引用 Infrastructure 类型,故一并排除,避免方法面板列出不可达类型。
/// </summary>
private static bool IsBusinessModuleAssembly(string name)
{
if (string.IsNullOrEmpty(name)) return false;
// 排除测试与基础设施程序集
if (name.EndsWith(".Tests", StringComparison.Ordinal)) return false;
if (name.EndsWith(".Test", StringComparison.Ordinal)) return false;
if (name.EndsWith(".Infrastructure", StringComparison.Ordinal)) return false;
// 宿主程序集本身不含可调用的 request 类型,且脚本不应耦合端点层
if (name.EndsWith(".Host", StringComparison.Ordinal)) return false;
// 必须匹配某个业务模块前缀("Prefix." 或恰好等于 "Prefix")
foreach (var p in _modulePrefixes)
{
if (name == p || name.StartsWith(p + ".", StringComparison.Ordinal)) return true;
}
return false;
}
private static ScriptTypeMeta BuildMeta(Type type) private static ScriptTypeMeta BuildMeta(Type type)
{ {
var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)