Bitzsoft.Integrations.IManage 1.0.5

dotnet add package Bitzsoft.Integrations.IManage --version 1.0.5
                    
NuGet\Install-Package Bitzsoft.Integrations.IManage -Version 1.0.5
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Bitzsoft.Integrations.IManage" Version="1.0.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Bitzsoft.Integrations.IManage" Version="1.0.5" />
                    
Directory.Packages.props
<PackageReference Include="Bitzsoft.Integrations.IManage" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Bitzsoft.Integrations.IManage --version 1.0.5
                    
#r "nuget: Bitzsoft.Integrations.IManage, 1.0.5"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Bitzsoft.Integrations.IManage@1.0.5
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Bitzsoft.Integrations.IManage&version=1.0.5
                    
Install as a Cake Addin
#tool nuget:?package=Bitzsoft.Integrations.IManage&version=1.0.5
                    
Install as a Cake Tool

Bitzsoft.Integrations.IManage

iManage Work 文档管理集成客户端 — REST API v2(Universal API)封装、OAuth2 认证、多租户 Token 刷新。

路径、动词与请求体对齐 iManage 官方 OpenAPI 规范;每个公开方法的 XML 注释中都标注了对应端点地址与调用示例,IntelliSense 悬停即可查看。

功能特性

  • 规范对齐 — 聚合库、工作区、文件夹、Tab、文档、用户、用户组、自定义字段、安全、分享共 10 个模块,81 个端点逐条核对官方规范
  • 多租户支持 — 通过 IIManageOptionsProvider 按租户动态解析配置,IIManageClientFactory.Create(tenantId) 创建租户隔离的客户端
  • 自动 Token 管理 — IManageAuthHandler 自动注入 Bearer Token,401 时刷新重试,Token 持久化由宿主实现 IIManageTokenStore
  • 流式分页 — Stream*Async 系列方法基于 IAsyncEnumerable,按规范的 limit / offset 自动翻页,内存占用恒定
  • 作用域搜索 — ScopedSearch*Async 对应 POST /{documents|workspaces|folders}/search,可指定返回字段以减小响应
  • 文档全生命周期 — 上传、下载、版本管理、检出/检入/锁定、复制/移动、引用、关联、安全 ACL、Name-Value Pairs、邮件分享
  • Resilience 管道 — 内置标准重试与熔断策略(AddStandardResilienceHandler)
  • 第三方请求日志 — 内置 RequestLogging DelegatingHandler,记录所有出站 HTTP 请求与响应,便于问题排查
  • 结构化异常 — IManageException 携带 IManageErrorCode、HTTP 状态码、API 错误码和请求 URL

安装

dotnet add package Bitzsoft.Integrations.IManage
<PackageReference Include="Bitzsoft.Integrations.IManage" Version="1.0.5" />

配置

{
  "IManage": {
    "Host": "https://imanage.example.com",
    "ClientId": "{{IMANAGE_CLIENT_ID}}",
    "ClientSecret": "{{IMANAGE_CLIENT_SECRET}}",
    "UserName": "{{IMANAGE_USER}}",
    "Password": "{{IMANAGE_PASSWORD}}",
    "DefaultCustomerId": "1",
    "DefaultLibraryId": "ACTIVE",
    "TimeoutMs": 300000,
    "TokenEndpoint": "/auth/oauth2/token",
    "ApiBasePath": "/work/api/v2",
    "TokenCacheMinutes": 30,
    "DefaultSecurity": "public",
    "DefaultPageSize": 100,
    "WorkspaceChildrenLimit": 300
  }
}

DefaultLibraryId 须为服务端真实库 ID(通常为小写,可先用 GetLibrariesAsync() 查看)。配置 UserName / Password 时走 Resource Owner Password Credentials 模式,否则走 Client Credentials。

TimeoutMs 是单次操作(含文件上传/下载全程)的总上限,默认 5 分钟以容纳大文件;交互式场景可调低。net8+ 的弹性管道按"单次尝试 2 分钟 / 总计 5 分钟"配置,GET 等安全方法的瞬时故障会自动重试,POST/PATCH/DELETE 永不自动重试。

注册服务

using Bitzsoft.Integrations.IManage;

// 基本注册(单租户)
services.AddIManage(options =>
{
    options.Host = "https://imanage.example.com";
    options.ClientId = "your-client-id";
    options.ClientSecret = "your-client-secret";
    options.DefaultLibraryId = "ACTIVE";
});

// 必须注册 IIManageTokenStore 实现(如 Redis、Memory 等)
services.AddSingleton<IIManageTokenStore, RedisIManageTokenStore>();

// 多租户场景:不传 configure,注册自定义 IIManageOptionsProvider(先后顺序均可,默认实现以 TryAdd 注册不会覆盖)
services.AddIManage();
services.AddSingleton<IIManageOptionsProvider, TenantIManageOptionsProvider>();
services.AddSingleton<IIManageTokenStore, RedisIManageTokenStore>();

单租户直接 factory.Create():租户键会归一为 IManageOptions.DefaultTenantId("default"),IIManageOptionsProvider / IIManageTokenStore / 刷新锁全链路以该键工作,自定义实现对它返回默认配置即可。未传 configure 又未注册自定义解析器时,默认解析器在首次解析时以明确消息快速失败。

第三方请求日志

内置 Bitzsoft.Integrations.RequestLogging 出站请求记录管道,默认使用 NullRequestLogStore 不持久化。

// ① 默认:启用记录管道但不持久化(日志丢弃)
services.AddIManage(options => { /* ... */ });

// ② 持久化:宿主注册 IRequestLogStore 实现后,所有出站请求自动落库
services.AddRequestLogging<MyRequestLogStore>(opts =>
{
    opts.MaxInMemoryBodyBytes = 64 * 1024; // 仅控制内存/加密临时文件切换,不截断正文
    opts.SensitiveFields.Add("mySecret");
});
services.AddIManage(options => { /* ... */ });

使用示例

查询工作区与文档

using Bitzsoft.Integrations.IManage;

using var client = _factory.Create();

// 按客户别名列出工作区(GET /workspaces)
var workspaces = await client.SearchWorkspacesAsync(new SearchWorkspaceInput { Custom1 = "10001", Limit = 50 });

// 探测某工作区(含子树)是否有文档(GET /documents?container_id=…&include_subtree=true&limit=1)
var probe = await client.SearchDocumentsAsync(new SearchDocumentsInput
{
    ContainerId = "ACTIVE!123456",
    IncludeSubtree = true,
    Limit = 1
});
var hasDocuments = probe.Data.Count > 0;

// 流式遍历工作区下全部文档(自动翻页)
await foreach (var doc in client.StreamDocumentsAsync(new SearchDocumentsInput { ContainerId = "ACTIVE!123456", Limit = 200 }))
    Console.WriteLine($"{doc.Id}  {doc.Name}.{doc.Extension}");

// 作用域搜索:全文检索并只取需要的字段(POST /documents/search)
var found = await client.ScopedSearchDocumentsAsync(new ScopedSearchRequest<DocumentSearchFilters>
{
    Limit = 50,
    ProfileFields = new() { ["document"] = ["name", "author", "edit_date"] },
    Filters = new() { ContainerId = "ACTIVE!123456", IncludeSubtree = true, Anywhere = "保密协议" }
});

上传与下载

using var client = _factory.Create();

// 上传(POST /folders/{id}/documents,multipart:profile 在前、file 在后)
await using var fileStream = File.OpenRead("/path/to/contract.pdf");
var document = await client.UploadDocumentAsync("ACTIVE!157954", new UploadDocumentInput
{
    Name = "合同草案",
    Extension = "pdf",
    Size = fileStream.Length,
    DocProfile = new DocProfileInput { Author = "JOHN.DOE", Class = "DOC" }
}, fileStream);

// 下载指定版本(版本号编码进文档 ID)
await using var content = await client.DownloadDocumentAsync(document.Id, version: 1);
await using var output = File.Create("downloaded.pdf");
await content.CopyToAsync(output);

检出、检入与安全

using var client = _factory.Create();

// 检出并下载(POST /documents/{id}/checkout,appname 必填)
await using var checkedOut = await client.CheckOutDocumentAsync("ACTIVE!1096273.1", new CheckOutInput { AppName = "MyApp" });

// 带内容检入(PUT /documents/{id}/file);不带内容则仅释放锁
await using var revised = File.OpenRead("/path/to/revised.pdf");
await client.CheckInDocumentAsync("ACTIVE!1096273.1", new CheckInInput { Comments = "完成修订" }, revised);

// 更新 ACL(POST /documents/{id}/security)
await client.SetDocumentSecurityAsync("ACTIVE!1096273.1", new UpdateSecurityInput
{
    Include = [new() { Id = "LEGAL-TEAM", AccessLevel = "read_write", Type = "group" }]
});

幂等同步:Ensure 系列(推荐用于定时同步 / 重跑)

iManage 不强制同级容器名称唯一,也未定义自定义字段重复创建的状态码——直接"检查→创建"在重跑或并发时会产生同名副本或重复写入。Ensure 系列方法以"读-比-写"实现幂等:已存在即复用且不修改既有对象,缺失才创建,创建被拒时复查而非重试。

using var client = _factory.Create();

// 自定义字段:先父后子,内容一致时零写入;子表(custom2 / custom30)的查找与更新自动携带规范要求的 ?parent_alias=
await client.EnsureCustomFieldAsync("custom1", "10001", "示例证券");
var matter = await client.EnsureCustomFieldAsync("custom2", "20099", "股权转让项目", parentAlias: "10001");

// 容器:同名文件夹/Tab 已存在则复用,不会再建一个副本
var tab = await client.EnsureTabAsync("ACTIVE!123456", new CreateTabInput { Name = "交易文件" });
var folder = await client.EnsureTabFolderAsync(tab.Value.Id, new CreateFolderInput { Name = "签署版", DefaultSecurity = "inherit" });
if (folder.Outcome == ContainerEnsureResult<FolderResponse>.OutcomeKind.Found)
    Console.WriteLine($"复用既有文件夹 {folder.Value.Id},其中文档不受影响");

同名多个时 Ensure 抛出 InvalidOperationException 而不是任选其一;需要修改既有容器属性请显式调用对应的 Update*Async。

已存在容器的 custom 补充:新建(Created)时 profile 随创建请求完整提交;对认领的既有容器(Found),iManage 规范不返回文件夹的当前 profile——"哪些 custom 缺失"无法判定,因此 Ensure 不做静默回填。推荐策略:

var result = await client.EnsureSubfolderAsync(parentId, new CreateFolderInput { Name = "尽调材料", Profile = expected });
if (result.Outcome == ContainerEnsureResult<FolderResponse>.OutcomeKind.Found)
{
    // 用 NVP 标签区分来源:系统创建的才补 profile,历史人工创建的不动
    var nvp = await client.GetFolderNameValuePairsAsync(result.Value.Id);
    if (nvp.TryGetValue("FolderTemplateId", out var tag) && tag.ValueKind == JsonValueKind.String)
        await client.UpdateFolderAsync(result.Value.Id, new UpdateFolderInput { Profile = expected });
}

Tab 的当前 profile 可经 TabResponse.Profile 读回,可先比对再补缺失的键。

多租户使用

// 通过工厂创建指定租户的客户端
using var client = _factory.Create(tenantId: "tenant-001");

var workspace = await client.GetWorkspaceAsync("ACTIVE!123456");

能力边界与行为约定

以下能力在 iManage Universal API v2 规范中不存在,本包不提供:审计事件流、法律保留、扩展元数据(Insight+)、文件夹安全复制、分享链接撤销。iManage 的"分享"是由服务端发送带附件/链接的邮件(ShareDocumentByEmailAsync)。

  • 取消语义:Stream*Async / 自动翻页的 Get*Async 在取消令牌触发时抛出 OperationCanceledException,绝不把半截列表当作完整结果返回。
  • 总条数:IManageResponse<T>.TotalCount 对应规范字段 total_count,只有在搜索输入设置 IncludeTotal = true(发出 total=true)时服务端才返回,否则为 null。
  • 子表自定义字段:对 custom2 / custom30 的单条查询与更新必须提供 parentAlias(规范要求 parent_alias),缺失时在发起请求前抛 ArgumentException。
  • 文件名:上传时中文等非 ASCII 文件名以 RFC 5987 的 filename* 传输,filename 仅放 ASCII 占位;库中文档名以 profile 的 name 为准。
  • 数值 / 布尔槽位:custom17–custom20 为 double?、custom25–custom28 为 bool?(规范类型);反序列化同时接受字符串形式的数字与 "true"/"false"/0/1 形式的布尔。
  • 文档历史:DocumentHistoryResponse 字段名以规范为准(User / UserFullName / Activity / ActivityCode / ActivityDate / Comments)。
  • 字段级错误:服务端 error.messages[] 中的 field 与原因会拼入 IManageException.Message,便于直接定位不合法的 profile 字段。

相关包

包名 说明
Bitzsoft.Integrations.Ldap LDAP 目录服务集成客户端
Bitzsoft.Integrations.Beisen 北森 HR 系统集成客户端
Bitzsoft.Integrations.EkuaiBao 易快报费用管理集成客户端
Bitzsoft.Integrations.TencentCcc 腾讯云呼叫中心集成客户端
Bitzsoft.Integrations.FileStorage 文件存储抽象层
Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Bitzsoft.Integrations.IManage:

Package Downloads
Bitzsoft.Integrations.All

Bitzsoft 第三方集成聚合包 — net5.0 包含传统连接器,net8.0+ 额外包含受上游 TFM 限制的 AI 模块

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.5 191 10/20/2026
1.0.4 161 9/23/2026
1.0.3 107 9/22/2026
1.0.2 172 8/29/2026
1.0.1 188 8/3/2026
1.0.0 177 8/2/2026
1.0.0-alpha.10 109 7/26/2026
1.0.0-alpha.9 94 7/12/2026
1.0.0-alpha.8 300 7/1/2026
1.0.0-alpha.7 115 6/16/2026
1.0.0-alpha.6 106 6/16/2026
1.0.0-alpha.5 97 6/14/2026
1.0.0-alpha.3 94 6/7/2026
1.0.0-alpha.2 90 5/29/2026
1.0.0-alpha.1 86 5/28/2026

1.0.5 — 按官方 OpenAPI 规范(iManage Work Universal API v2,由服务器 /api-docs 提供)全量对齐,81 个动词+路径组合与规范逐条核对无偏差。
修复:分页统一为 limit/offset(原 start_index/max_results 被服务端忽略导致流式查询静默截断);安全 ACL 更新由 PATCH 改为 POST;检出改 POST /checkout(原 /check-out 不存在);提升版本改 /versions/promote;文档关联改 related-documents;文档引用改 /folders/{id}/documents;组成员增删改 PUT /members;上传/新建版本的 multipart 顺序与 doc_profile 结构修正;复制请求体字段修正为 folder_id。
新增:三个资源的 scoped search(POST /{documents|workspaces|folders}/search)、已检出文档列表、文档内容替换(PUT /file)、锁定/解锁、工作区模板(GET /templates)、工作区/Tab 根级建文件夹、文件夹移动、受托者查询(GET /trustees)、文档 NVP 读取、Tab 安全、custom2 子别名查询(GET /customs/custom2/{child}?parent_alias={parent})。
完善:工作区创建/更新支持 custom1–custom30 与改名(对齐规范全字段);文件夹/Tab 的 profile 支持任意附加键并提供 ContainerProfileInput.FromDictionary;UserResponse 补 user_num。
幂等:新增 EnsureCustomFieldAsync(自定义字段读-比-写 upsert,先父后子、零写入、竞态复查)、EnsureSubfolder/EnsureWorkspaceFolder/EnsureTab/EnsureTabFolder(容器 find-or-create,同名复用不修改、同名多个报错)与对应只读 TryFind 系列(预览哪些会复用/新建/冲突),供同步重跑与历史数据认领场景使用;修正 customs 的 parent 为规范要求的对象形状。
命名:DI 注册方法更名为 AddIManage(对齐全仓库 Add{产品}{类别} 惯例),旧名 AddTNTIManage 保留为 [Obsolete] 别名。
发版终审修复:子表 custom2/custom30 的单条 GET/PATCH 按规范携带 parent_alias(原 /custom1/{p}/custom2/{c} 为未文档化的遗留路由且不适用于 custom30);分页流在取消令牌触发时抛出而非静默返回截断数据;factory.Create() 的 null 租户归一为 IManageOptions.DefaultTenantId,修复单租户经 DI 管道必失败的问题;TimeoutMs 在 DI 路径下真正生效(默认提高为 5 分钟),弹性管道尝试/总超时放宽为 2/5 分钟以容纳大文件;非 ASCII 文件名改走 RFC 5987 filename*,含引号文件名不再崩溃;响应顶层带 warnings/default_security/total_count 等兄弟字段时仍正确解包 data;DocumentHistoryResponse 字段名修正为规范的 user/user_fullname/activity/activity_date/comments/activity_code;分页元数据改读规范字段 total_count(TotalCount,可经 IncludeTotal 请求);GroupResponse 显示名改读 full_name;custom17–20 改为 double?、custom25–28 改为 bool?;反序列化允许字符串形式数字与宽松布尔;error.messages[] 字段级错误进入异常消息;用户名/别名等路径段做 URL 转义;上传缺 Name、组成员为 null/空白等在发起请求前拒绝;DI 的默认 IIManageOptionsProvider 改为 TryAdd 且缺配置时快速失败。
移除(破坏性变更):审计、法律保留、扩展元数据(Insight+)、文件夹安全复制、分享链接撤销——规范中不存在对应端点。