Tool Connectors 是一组可选的厂商 SDK 防腐层。当前源码交付 Contracts 与 Infrastructure 两个项目,提供 Crawler、OCR、iManage 三组 Port、DTO、真实 Adapter 和不可用 Adapter;它没有 Application 用例、HTTP Endpoint 或业务流程。
1. 架构位置
Port 隔离厂商类型;Adapter 翻译调用与结果;Host 决定 SDK、配置和生命周期;业务归属模块负责授权、可信租户、输入安全、幂等、审计、保留与结果解释。
2. 代码地图
| 项目 | 当前责任 | 不负责 |
|---|---|---|
BitzOrcas.Platform.ToolConnectors.Contracts | Port、请求/结果 DTO、错误、权限、Feature、模块标记 | SDK、工作流、Endpoint |
BitzOrcas.Platform.ToolConnectors.Infrastructure | 三个 SDK Adapter、条件 DI、Unavailable 实现 | 授权、审计、输入治理 |
BitzOrcas.Api | 仅引用 Contracts 供治理发现 | 当前未注册 Adapter/SDK |
三个 SDK 包都以 1.0.0-alpha.8 引入 Infrastructure。契约项目不引用这些包,因此消费者只需依赖稳定 Port。
3. 能力矩阵
| 能力 | 当前源码已有 | 当前不能承诺 |
|---|---|---|
| Crawler | 打开 URL、查元素、截图、执行脚本 | SSRF 防护、浏览器会话隔离、脚本沙箱 |
| OCR | 文件、字节、Base64、URL、区域、验证码 | 文件授权、大小/MIME/维度限制、URL 策略 |
| iManage | 上传、下载、签出、签入、历史 | 应用授权、幂等、审计、对账、流式大文件 |
| 条件 DI | Enabled + SDK 注册双条件;否则不可用实现 | Host 自动注册 SDK、自动启用产品功能 |
| 治理目录 | 三权限、三个默认关闭 Feature | 请求级权限/Feature 执行 |
4. 三个边界为什么必须分层
连接器回答“怎样调用供应商”,业务模块回答“谁可以在什么条件下调用”。例如 OCR Adapter 能识别字节,但不知道字节是否属于当前用户、是否通过恶意内容扫描、是否允许送往外部引擎。
正确调用链应当包含:
- Endpoint 完成认证、请求限流与稳定错误映射;
- Application 从可信上下文取得 TenantId 和 ActorId;
- owner 模块校验对象归属、状态、用途和审批;
- 输入策略限制 URL、大小、类型、维度和脚本;
- Port 执行供应商调用;
- owner 持久化外部标识、审计、幂等结果和对账状态;
- 输出经过脱敏、分类和保留策略再返回。
5. 当前 OCR 调用示例
下面的代码只展示真实 Port 签名;前置策略是调用方必须补齐的生产边界。
// ① bytes 必须由 owner 在归属、类型、大小和扫描校验后提供。byte[] bytes = await trustedInput.ReadBoundedBytesAsync(cancellationToken);
// ② 当前 IOcrPort 直接接收 byte[],没有 LanguageHints 或虚构请求对象。Result<OcrResultDto> result = await ocr.ExtractTextFromBytesAsync(bytes, cancellationToken);
// ③ 不可用和受支持的 Provider 异常都以 Result failure 返回。if (result.IsFailure) return Problem(result.Error);
// ④ Text、RawText 与 Regions 都是外部不可信输出,展示前要分类和脱敏。return Ok(Sanitize(result.Value!));6. 错误模型
三个 Unavailable Port 对异步方法返回 ProviderUnavailable,让未配置状态失败关闭。OCR 的同步 GenerateCaptcha 是例外:它返回 DTO 而非 Result<T>,不可用实现会抛 InvalidOperationException。
真实 Adapter 只捕获有限的 SDK/IO 异常,未覆盖的参数、网络、格式、内存或流异常仍可能越过边界。iManage 错误码还拼接供应商 Code,当前并非完全稳定的错误分类。
7. 配置键不是 Feature Gate
运行时适配器选择读取:
Connectors:iManage:EnabledConnectors:Ocr:EnabledConnectors:Crawler:Enabled
模块目录另外声明:
tool-connectors.imanagetool-connectors.ocrtool-connectors.crawler
前者只参与 DI,后者目前没有被调用路径消费。生产实现必须明确配置启用、租户 Feature 授权、角色权限是三个不同层次。
8. 权限目录不是授权执行
当前目录包含 tool-connectors.imanage.execute、tool-connectors.ocr.execute、tool-connectors.crawler.execute。因为模块没有 Application/Endpoint,源码没有用例把这些权限绑定到调用。
Platform Application 还保留一份重复的旧治理声明,注释称 owner-local 目录尚不存在,而当前 owner-local 定义已经存在。这是治理迁移债务;GA 前应只保留一个权威定义并增加唯一性测试。
9. 生命周期与租户语义
iManage 通过 IManageClientFactory.Create(tenantId) 选择租户客户端,但 TenantId 由 Port 参数传入;当前没有 Application 层保证它来自认证上下文。
Crawler Adapter 是 scoped,底层 ICrawlingService 预期长期复用。Port 没有 session handle,打开页面、查找、截图、执行脚本都作用于隐含的“当前浏览器”。并发租户可能互相改写该状态,不能作为隔离保证。
OCR 本身无租户参数。租户隔离完全属于调用方的数据获取和输出保留路径。
10. 可观测性现状
Adapters 记录部分 Debug/Warning/Error 日志,但没有标准连接器指标、trace 属性、健康探针或供应商 request ID 契约。原始 URL、异常消息、文档属性和 OCR 文本可能含敏感信息,日志策略必须先定义再扩展。
最低指标应区分:调用量、耗时、超时、不可用、供应商失败、输入拒绝、在途数量和未知结果;标签不得直接包含完整 URL、文档名或 TenantId。
11. 深入阅读
12. 源码核查
# 只应看到 Contracts 与 Infrastructure 项目。find src/Platform/ToolConnectors -maxdepth 2 -name '*.csproj' -print
# Host 当前不会命中 Adapter 注册或 SDK 注册。rg -n "AddBitzOrcasToolConnectorsAdapters|AddTNTIManage|AddOCRService|AddCrawlingService" \ src/Hosts -g '*.cs'
# 检查 Port 的真实消费者;当前预期只有定义、适配器和不可用实现。rg -n "ICrawlerPort|IOcrPort|IIManageArchivePort" src -g '*.cs' \ --glob '!**/bin/**' --glob '!**/obj/**'