契约版本:1.20 状态:已确认,LibreOffice 离线文档转换与 PDF 预览接口基线 外部字段风格:
camelCase
数据库字段风格:snake_case
本文件是 /api/v1 的唯一人工可读接口契约。执行会话必须完整读取 DMS_FUNCTION_CONTRACT.md 和本文件。
后续建立的 backend/openapi/openapi.yaml 是本文件的机器可读镜像,不得另行发明字段、路径或枚举。两者不一致时不得自行选择,必须提交统领会话裁决。
以下旧路径明确废止,不得实现,也不得由前端调用:
/api/v1/documents/{id}/children
/api/v1/documents/{id}/materials
/api/v1/documents/recent
/api/v1/documents/search
/api/v1/documents/{id}/restore
/api/v1/attachments/{id}/preview
/api/v1/attachments/{id}/download
主案子方案、主案附件、搜索、最近更新和文件读取必须使用本文定义的规范路径。
其中 /api/v1/documents/{id}/restore 永久废止,由以下独立回收站资源替代,不提供重定向、别名或兼容调用:
GET /api/v1/recycle-bin/documents
POST /api/v1/recycle-bin/documents/{id}/restore
Base path: /api/v1
Authorization: Bearer <accessToken>
X-Request-Id: <UUID,可选>
除登录和健康检查外,所有接口需要有效 Token。
客户端未传 X-Request-Id 时由后端生成。响应头和 JSON 响应体均返回同一个请求 ID:
X-Request-Id: <requestId>
BIGINT;Z 的 ISO 8601,例如 2026-07-23T03:00:00.000Z;{
"code": "OK",
"message": "success",
"data": {},
"requestId": "8ed3e305-73a8-4af7-8f39-1b86b9a2dad2"
}
创建成功使用 HTTP 201,其他普通成功使用 200。删除成功仍返回上述 JSON,不使用 204。
{
"items": [],
"page": 1,
"pageSize": 20,
"total": 0,
"totalPages": 0
}
默认值:
page=1pageSize=20pageSize=100{
"code": "DATA_VERSION_CONFLICT",
"message": "数据已被其他用户修改,请刷新后重试",
"details": {
"currentRowVersion": 3
},
"requestId": "8ed3e305-73a8-4af7-8f39-1b86b9a2dad2"
}
details 无附加信息时为 null,不得省略。
| HTTP | 含义 |
|---|---|
| 200 | 查询、更新、删除或普通操作成功 |
| 201 | 创建成功 |
| 400 | 参数、结构或业务前置条件格式错误 |
| 401 | 未登录、Token无效或已失效 |
| 403 | 角色、密级或操作权限不足 |
| 404 | 对象不存在或已逻辑删除 |
| 409 | 数据版本、唯一性、引用关系等冲突 |
| 413 | 文件、批量数量或请求总体过大 |
| 415 | 文件格式或预览格式不支持 |
| 500 | 未处理的服务器错误 |
更新、删除、权限全量保存、挂载和解除挂载请求必须携带目标资源的 rowVersion。版本不一致返回:
HTTP 409
code = DATA_VERSION_CONFLICT
成功修改后返回新的 rowVersion。
预览和下载成功时不使用 JSON 包装。
PDF预览:
Content-Type: application/pdf
Content-Disposition: inline; filename*=UTF-8''...
X-Request-Id: ...
下载:
Content-Type: 实际MIME类型或application/octet-stream
Content-Disposition: attachment; filename*=UTF-8''...
X-Request-Id: ...
文件接口失败时返回普通 JSON 错误响应。
USER
ADMIN
DOCUMENT_BROWSER
BACKEND_MANAGEMENT
AUDIT_LOG
MAIN
SUB_PLAN
ATTACHMENT
DRAFT
PUBLISHED
ARCHIVED
PUBLIC
INTERNAL
SECRET
CONFIDENTIAL
TOP_SECRET
ALL_AUTHENTICATED
ORGANIZATION
CUSTOM
POLICY
WORK_STANDARD
TABLE
DIAGRAM
OTHER
(1.19 起废止:借阅授权仅面向用户主体,doc_permission.subject_type 列与 ORG/USER 枚举一并下线,接口与数据库均不再存在主体类型维度。)
VIEW
DOWNLOAD
EDIT
CONFIG_PERMISSION
DELETE
BIND_ATTACHMENT
UNBIND_ATTACHMENT
RESTORE_DOCUMENT
SCENE
STYLE
SITUATION
VERSION
OTHER
用户、组织和分类统一使用:
ENABLED
DISABLED
SUCCESS
FAILURE
LOGIN
LOGOUT
VIEW_DOCUMENT
DOWNLOAD_DOCUMENT
UPLOAD_DOCUMENT
BATCH_IMPORT
EDIT_DOCUMENT
DELETE_DOCUMENT
CREATE_CATEGORY
EDIT_CATEGORY
DELETE_CATEGORY
CHANGE_PERMISSION
BIND_ATTACHMENT
UNBIND_ATTACHMENT
AUTH
CATEGORY
DOCUMENT
ATTACHMENT
PERMISSION
ATTACHMENT_BINDING
{
"id": "1",
"username": "admin",
"realName": "系统管理员",
"organizationId": "10",
"organizationName": "机关",
"roleCode": "ADMIN",
"securityLevel": "TOP_SECRET",
"status": "ENABLED",
"allowedModules": [
"DOCUMENT_BROWSER",
"BACKEND_MANAGEMENT"
]
}
模块映射(allowedModules)按“用户对文档的实际权限”动态计算:
USER:始终包含 DOCUMENT_BROWSER;当角色权限矩阵对任一已发布主案命中管理位
(编辑/删除/授权/挂载/解除挂载,且该主案对用户可查看)时,追加 BACKEND_MANAGEMENTADMIN:DOCUMENT_BROWSER、BACKEND_MANAGEMENT、AUDIT_LOG、ORGANIZATION_USER_MANAGEMENT前端必须使用后端返回的 allowedModules,不得根据角色自行补全模块。
{
"id": "100",
"categoryCode": "SCENE_A",
"categoryName": "XXXX场景A",
"categoryType": "SCENE",
"parentId": null,
"categoryPath": "/XXXX场景A",
"sortNo": 10,
"documentCount": 3,
"rowVersion": 0,
"children": []
}
{
"id": "1001",
"documentName": "2024年度综合应急预案",
"summary": "方案内容概述",
"documentType": "MAIN",
"status": "PUBLISHED",
"securityLevel": "SECRET",
"borrowSummary": "张三、李四",
"categoryId": "100",
"categoryName": "XXXX场景A",
"categoryPath": "/XXXX场景A",
"parentDocumentId": null,
"rootDocumentId": "1001",
"attachmentType": null,
"fileExtension": "docx",
"tags": ["应急", "年度"],
"childCount": 3,
"attachmentCount": 2,
"viewCount": 12,
"downloadCount": 4,
"createdByName": "系统管理员",
"createdAt": "2026-07-23T03:00:00.000Z",
"updatedAt": "2026-07-23T03:00:00.000Z",
"rowVersion": 2,
"allowedActions": ["VIEW", "DOWNLOAD", "EDIT"]
}
ATTACHMENT 返回时:
categoryId/categoryName/categoryPath/parentDocumentId/rootDocumentId 为 null;securityLevel 固定 PUBLIC;attachmentType 必填。borrowSummary 为借阅摘要:当前生效(canView=true 且未到期)借阅人姓名以 、 连接,无生效借阅时为 未配置 占位;无查看权限的列表行以 - 占位(借阅名单属敏感信息不下发)。仅作展示,不作为权限判定依据。permissionSummary.borrowerCount 与摘要同口径统计生效借阅人数。
DocumentDetail 包含 DocumentSummary 的全部字段,并增加:
{
"originalFileName": "2024年度综合应急预案.docx",
"mimeType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"fileSize": 204800,
"fileHash": "SHA-256十六进制字符串",
"permissionSummary": {
"borrowerCount": 3,
"inheritedFromMainPlan": false
},
"contentExtractStatus": "SUCCESS",
"contentExtractError": null,
"contentExtractedAt": "2026-08-27T03:00:00.000Z"
}
contentExtractStatus/contentExtractError/contentExtractedAt 仅管理员(ADMIN)详情响应返回,用于"编辑"界面排障与重新解析:
contentExtractStatus:SUCCESS/EMPTY/UNSUPPORTED/FAILED 之一;contentExtractError:提取失败的脱敏原因(异常类型+首行信息,已去除绝对路径),非 FAILED 时为 null;contentExtractedAt:最近一次正文提取时间(UTC),未提取过为 null;{
"id": "1001",
"documentName": "2024年度综合应急预案",
"documentType": "MAIN",
"originalFileName": "2024年度综合应急预案.docx",
"categoryId": "100",
"categoryName": "XXXX场景A",
"parentDocumentId": null,
"securityLevel": "SECRET",
"documentStatus": "PUBLISHED",
"deletedAt": "2026-07-26T03:00:00.000Z",
"updatedBy": "1",
"updatedByName": "系统管理员",
"deletedBy": {
"userId": "1",
"username": "admin",
"realName": "系统管理员",
"organizationName": "机关"
},
"rowVersion": 3
}
categoryId、categoryName、parentDocumentId、updatedBy、updatedByName和deletedBy允许为 null。deletedBy存在时,其四个字段也分别允许为 null。
删除人取与目标ID、目标类型匹配的最近一次成功 DELETE_DOCUMENT 审计快照:
MAIN和SUB_PLAN匹配 targetType=DOCUMENT;ATTACHMENT匹配 targetType=ATTACHMENT;null;updatedBy 或 updatedByName 冒充删除人。{
"document": {},
"restoredPermissionCount": 2,
"skippedPermissionCount": 1,
"restoredBindingCount": 3,
"skippedBindingCount": 1
}
document使用 DocumentDetail 字段,但恢复响应必须通过纯序列化生成,不得增加 viewCount、写入 VIEW_DOCUMENT 审计或触发文档详情查询的查看副作用。
GET /api/v1/health无需认证。
{
"code": "OK",
"message": "success",
"data": {
"service": "dms",
"status": "UP"
},
"requestId": "uuid"
}
健康检查不得泄露数据库密码、Token密钥和磁盘绝对路径。
POST /api/v1/auth/login无需认证。
请求:
{
"username": "admin",
"password": "password",
"keepSignedIn": true
}
成功:
{
"code": "OK",
"message": "success",
"data": {
"accessToken": "jwt",
"tokenType": "Bearer",
"expiresIn": 7200,
"user": {}
},
"requestId": "uuid"
}
user 使用 UserSummary。用户名或密码错误统一返回 401 INVALID_CREDENTIALS,不得泄露用户是否存在。
Token有效期固定为:
keepSignedIn=false:expiresIn=7200;keepSignedIn=true:expiresIn=604800。第一阶段不签发刷新令牌。
POST /api/v1/auth/logout请求体为空。成功后递增当前用户 authVersion,使该用户全部旧Token失效。
GET /api/v1/users/me返回当前 UserSummary。
GET /api/v1/organizations/tree查询参数:
keyword:可选,组织名称或编码;status:可选。返回组织树数组,每个节点至少包含:
{
"id": "10",
"orgCode": "ORG-10",
"orgName": "作战部",
"parentId": null,
"orgPath": "/作战部",
"sortNo": 10,
"status": "ENABLED",
"children": []
}
GET /api/v1/users查询参数:
organizationIdincludeDescendants,默认 falsekeywordstatuspagepageSize返回 PageResult<UserSummary>。
GET /api/v1/categories/tree查询参数:
keyword:只搜索分类名称或编码;status:可选。返回 CategoryNode[]。
documentCount 表示该分类完整有效子树内所有有效主体方案(MAIN)数量,含本分类与全部后代分类,不按当前用户密级/权限过滤,所有登录用户口径一致;计数沿父链穿过停用分类节点累加。
POST /api/v1/categories管理员接口。
{
"categoryCode": "SCENE_A",
"categoryName": "XXXX场景A",
"categoryType": "SCENE",
"parentId": null,
"sortNo": 10
}
返回新建 CategoryNode,HTTP 201。
PUT /api/v1/categories/{id}管理员接口。
{
"categoryName": "新名称",
"categoryType": "SCENE",
"parentId": null,
"sortNo": 20,
"rowVersion": 1
}
移动分类不能形成环。成功返回更新后的 CategoryNode。
DELETE /api/v1/categories/{id}查询参数:
rowVersion=<integer>
存在有效子分类或有效主案、子方案时返回 409 CATEGORY_IN_USE。
GET /api/v1/documents查询参数:
documentType:逗号分隔,可为 MAIN,SUB_PLAN;不用于查询附件;categoryIdkeywordsecurityLevelstatusupdatedFromupdatedTosortBy:仅允许 documentName/createdAt/updatedAt/viewCount/downloadCountsortDirection:asc/descpagepageSize页签调用规则:
主案列表: documentType=MAIN
全部方案: documentType=MAIN,SUB_PLAN
最近更新: documentType=MAIN,SUB_PLAN&sortBy=updatedAt&sortDirection=desc
关键词搜索也使用本接口,不建立独立 /search 路径。
updatedFrom作用为 updated_at >= updatedFrom,updatedTo作用为 updated_at <= updatedTo;同时提供时为闭区间,且 updatedFrom不得晚于 updatedTo。时间参数必须是包含明确时区的ISO 8601时间,前端统一发送UTC Z格式;空字符串、无时区时间、非法格式或反向区间返回 400 INVALID_ARGUMENT。时间条件在数据库查询和分页之前执行。
返回 PageResult<DocumentSummary>。
POST /api/v1/documents管理员接口,multipart/form-data:
file:一个文件;metadata:JSON字符串。主案 metadata:
{
"documentName": "主案名称",
"documentType": "MAIN",
"summary": "概述",
"categoryId": "100",
"securityLevel": "SECRET",
"tags": ["标签"]
}
子方案 metadata 增加:
{
"documentType": "SUB_PLAN",
"parentDocumentId": "1001"
}
子方案 parentDocumentId 必须指向有效主案。返回 DocumentDetail,HTTP 201。
名称唯一性与覆盖:
MAIN 的 documentName 全局唯一;SUB_PLAN 的 documentName 在同一 parentDocumentId 下唯一;409 DOCUMENT_NAME_CONFLICT,details 包含 existingDocumentId、existingDocumentName、existingRowVersion、documentType、parentDocumentId;overwriteDocumentId 和 rowVersion;只提交其中一个返回 400 INVALID_ARGUMENT;409 DATA_VERSION_CONFLICT;DOCUMENT_NAME_CONFLICT。POST /api/v1/documents/batch-import管理员接口,multipart/form-data:
files:重复文件字段;items:JSON数组字符串,与 files 按顺序一一对应。每个 items[n] 使用单文件 metadata 的结构。数组长度与文件数量不一致返回 400 BATCH_MANIFEST_MISMATCH。
每个文件独立事务,整体 HTTP 200:
{
"code": "OK",
"message": "success",
"data": {
"total": 2,
"successCount": 1,
"failureCount": 1,
"items": [
{
"index": 0,
"originalFileName": "a.docx",
"success": true,
"document": {}
},
{
"index": 1,
"originalFileName": "b.exe",
"success": false,
"errorCode": "UNSUPPORTED_FILE_TYPE",
"errorMessage": "不支持的文件类型"
}
]
},
"requestId": "uuid"
}
GET /api/v1/documents/{id}仅用于 MAIN 和 SUB_PLAN,返回 DocumentDetail。
PUT /api/v1/documents/{id}只编辑元数据,不替换文件。按文档权限校验:需通过密级检查且具备该文档对应操作权限(管理员恒通过;普通用户由角色矩阵×密级×部门范围与文档授权 OR 叠合决定)。设密主案另需通过对应操作密码锁。(EDIT)
{
"documentName": "新名称",
"summary": "新概述",
"categoryId": "100",
"securityLevel": "SECRET",
"status": "PUBLISHED",
"tags": ["标签1", "标签2"],
"rowVersion": 2,
"organizationId": "10"
}
不允许修改 documentType、parentDocumentId、rootDocumentId 和文件身份字段。
organizationId 可选,用于调整文档归属部门(所属单位):缺省沿用现有归属;原样回传现有归属视为未变更;
调整到其他部门仅 ADMIN 可执行(普通用户提交其他部门返回 400 INVALID_ARGUMENT),目标部门必须存在且启用
(否则 404 RESOURCE_NOT_FOUND),子方案与共享附件编辑遵循同一规则。
DELETE /api/v1/documents/{id}按文档权限校验:需通过密级检查且具备该文档对应操作权限(管理员恒通过;普通用户由角色矩阵×密级×部门范围与文档授权 OR 叠合决定)。设密主案另需通过对应操作密码锁。(DELETE)
查询参数:
rowVersion=<integer>
主案存在有效子方案时返回 409 MAIN_PLAN_HAS_CHILDREN。成功返回:
{
"id": "1001",
"deleted": true
}
GET /api/v1/main-plans/{id}/sub-plans查询参数:
keywordstatusupdatedFromupdatedTopagepageSizesortBysortDirection时间范围作用于直属子方案自身的 updated_at,语义和错误规则与9.1相同。返回 PageResult<DocumentSummary>。id 不是有效主案时返回 404 MAIN_PLAN_NOT_FOUND。
POST /api/v1/documents/{id}/re-extract管理员接口(ADMIN),主案/子案/共享附件通用,无请求体。
按当前存储文件重新解析正文并回写提取结果:
doc/wps/xls/xlsx/et)先经 LibreOffice 转 PDF 再提取;pdf/docx 直接提取;其余类型恒为 UNSUPPORTED;contentText、contentExtractStatus、contentExtractError、contentExtractedAt,并同步重建 search_text(名称+概述+标签+正文);FAILED 并携带脱敏失败原因(异常类型+首行信息,已去除绝对路径);rowVersion 递增,审计复用 EDIT_DOCUMENT(detail.scope=reExtractContent);404 FILE_NOT_FOUND。成功返回 DocumentDetail,HTTP 200。
PDF 提取的换行处理:PDF 复制文本时会在段内软换行处插入 \n。服务端提取时优先按版面结构识别段落——利用 PyMuPDF 的块(block)边界作为段落边界(标题、落款等因字号或间距不同独立成块,不依赖标点猜测),块内合并软换行(相邻行主字号相对差 ≥20% 视为段落边界);当整页为逐行等距同字号的退化网格(如扫描件文本层逐行定位)时回退纯文本启发式——空行、句末标点结尾、条目/章节编号开头视为段落边界保留换行,其余视为段内软换行直接合并(合并处两侧均为 ASCII 字母数字时补一个空格,中文文本直接相连)。页与页之间以空行分隔。
GET /api/v1/attachments查询参数:
keywordattachmentTypefileExtensionupdatedFromupdatedTosortBysortDirectionpagepageSize返回 PageResult<DocumentSummary>,只包含有效 ATTACHMENT。
该列表对所有登录用户开放且全量可见(支撑全局检索按标题命中全部附件)。普通用户返回的 allowedActions 为空集,且不返回 description 与 contentSnippet;上传、编辑、删除等写操作仍仅 ADMIN,附件内容访问由详情、预览、下载接口按挂载主案权限独立拦截。
POST /api/v1/attachments管理员接口,multipart/form-data:
filemetadata
{
"documentName": "附件名称",
"attachmentType": "POLICY",
"summary": "说明",
"tags": ["制度"]
}
后端强制:
documentType=ATTACHMENT
securityLevel=PUBLIC
categoryId=null
返回 DocumentDetail,HTTP 201。
POST /api/v1/attachments/batch-import与文档批量导入相同,使用重复 files 和顺序对应的 items。每个 item 使用附件 metadata 结构,每文件独立事务。
GET /api/v1/attachments/{id}返回附件 DocumentDetail。
PUT /api/v1/attachments/{id}按文档权限校验(EDIT;共享附件仅管理员可维护):
{
"documentName": "新名称",
"attachmentType": "WORK_STANDARD",
"summary": "新说明",
"tags": ["规范"],
"rowVersion": 1,
"organizationId": "10"
}
不得修改附件固定密级、固定可见范围和文件身份。organizationId 可选,语义与9.5一致:缺省沿用现有归属,
调整到其他部门仅 ADMIN 可执行。
DELETE /api/v1/attachments/{id}查询参数:
rowVersion=<integer>
存在有效挂载时:
HTTP 409
code = ATTACHMENT_IN_USE
details.mountedPlanCount
details.mainPlans
GET /api/v1/attachments/{id}/main-plans返回:
{
"items": [
{
"id": "1001",
"documentName": "主案名称",
"categoryName": "XXXX场景A",
"securityLevel": "SECRET"
}
],
"total": 1
}
GET /api/v1/main-plans/{id}/attachments返回当前主案有效挂载附件,支持:
keywordattachmentTypeupdatedFromupdatedTopagepageSize每项使用附件 DocumentSummary,增加:
{
"bindingId": "5001",
"bindingSortNo": 10,
"mountedPlanCount": 3
}
时间范围作用于附件文档自身的 updated_at,不使用 doc_attachment_binding的创建时间、更新时间或 bindingSortNo;语义和错误规则与9.1相同。
POST /api/v1/main-plans/{id}/attachments/bind按文档权限校验:需通过密级检查且具备该文档对应操作权限(管理员恒通过;普通用户由角色矩阵×密级×部门范围与文档授权 OR 叠合决定)。设密主案另需通过对应操作密码锁。(主案 BIND_ATTACHMENT):
{
"attachmentIds": ["2001", "2002"],
"mainPlanRowVersion": 3
}
请求内重复ID自动去重;已经挂载的附件不重复创建。
{
"createdCount": 1,
"existingCount": 1,
"attachmentCount": 5,
"mainPlanRowVersion": 4
}
DELETE /api/v1/main-plans/{id}/attachments/{attachmentId}按文档权限校验:需通过密级检查且具备该文档对应操作权限(管理员恒通过;普通用户由角色矩阵×密级×部门范围与文档授权 OR 叠合决定)。设密主案另需通过对应操作密码锁。(主案 UNBIND_ATTACHMENT)
查询参数:
mainPlanRowVersion=<integer>
逻辑删除挂载关系,不删除附件。返回最新附件计数和主案版本。
1.19 起权限模块收敛为借阅语义:仅面向用户主体的限时授权,操作位只有查看/下载两项,时限必选且最长 7 天;每条授权可选挂一份 PDF 审批单凭证。角色权限矩阵与单文档借阅 ACL 为 OR 叠加;借阅到期即失效(读路径惰性判定 expiresAt > now,无需定时任务)。原可见范围三模式(ALL_AUTHENTICATED/ORGANIZATION/CUSTOM)、组织主体授权与 canEdit/canManagePermission/canDelete 操作位全部下线。
GET /api/v1/documents/{id}/permissions按文档权限校验:需通过密级检查且具备该文档对应操作权限(管理员恒通过;普通用户由角色矩阵×密级×部门范围与文档授权 OR 叠合决定)。设密主案另需通过对应操作密码锁。(CONFIG_PERMISSION)
主案返回:
{
"documentId": "1001",
"sourceDocumentId": "1001",
"inherited": false,
"documentRowVersion": 3,
"entries": [
{
"id": "7001",
"subjectId": "10",
"subjectName": "张三",
"canView": true,
"canDownload": true,
"expiresAt": "2026-09-10T03:00:00.000Z",
"voucherFileName": "借阅审批单.pdf",
"voucherFileSize": 36752,
"voucherUploadedAt": "2026-09-03T03:00:00.000Z"
}
]
}
entries 按 subjectId 升序返回全部未删除授权(含已到期条目,由前端标记"已到期");expiresAt 为 UTC ISO(Z 结尾)到期时刻;voucherFileName/voucherFileSize/voucherUploadedAt 无凭证时均为 null。子方案可调用该查询接口,但返回所属主案权限:
sourceDocumentId 为主案ID;inherited=true。共享附件调用返回 400 ATTACHMENT_HAS_NO_ACL。
PUT /api/v1/documents/{id}/permissions按文档权限校验:需通过密级检查且具备该文档对应操作权限(管理员恒通过;普通用户由角色矩阵×密级×部门范围与文档授权 OR 叠合决定)。设密主案另需通过对应操作密码锁。(CONFIG_PERMISSION)
只允许对 MAIN 调用。子方案返回 409 SUB_PLAN_PERMISSION_INHERITED。
{
"documentRowVersion": 3,
"entries": [
{
"subjectId": "10",
"canView": true,
"canDownload": true,
"expiresAt": "2026-09-10T03:00:00.000Z"
}
]
}
规则:
documentRowVersion 和 entries;subjectId/canView/canDownload/expiresAt,两个动作布尔值均不允许省略;subjectId 必须指向存在、未删除且启用的用户,否则 404 RESOURCE_NOT_FOUND("借阅用户不存在、已删除或已禁用");expiresAt 必须是 Z 结尾的 UTC 时间字符串,必须晚于当前时间,且不超过当前时间 +7 天(服务端含 60 秒容差,覆盖前端分钟粒度拾取舍入),违反返回 400 INVALID_ARGUMENT;400 DUPLICATE_PERMISSION_SUBJECT;CHANGE_PERMISSION)。POST /api/v1/documents/{id}/permissions/entries/{entryId}/voucher上传(或替换)指定授权条目的审批单凭证。校验口径与 12.2 相同(仅主案;CONFIG_PERMISSION + 密级 + 密码锁)。
multipart/form-data,必须且只能包含文件字段 file,不得携带任何表单字段,否则 400 INVALID_ARGUMENT("请求必须且只能包含文件 file");415 UNSUPPORTED_FILE_TYPE;DMS_MAX_FILE_SIZE 约束,超限返回 413 PAYLOAD_TOO_LARGE("审批单超过允许大小");404 RESOURCE_NOT_FOUND("授权条目不存在");UPLOAD_PERMISSION_VOUCHER(目标 PERMISSION)。成功返回:
{
"documentId": "1001",
"entryId": "7001",
"voucherFileName": "借阅审批单.pdf",
"voucherFileSize": 36752,
"voucherUploadedAt": "2026-09-03T03:05:00.000Z"
}
GET /api/v1/documents/{id}/permissions/entries/{entryId}/voucher下载指定授权条目的审批单凭证。校验口径与 12.3 相同。
application/pdf 附件二进制(Content-Disposition: attachment,含 nosniff);404 RESOURCE_NOT_FOUND("该授权未上传审批单");文件缺失返回 404("审批单文件不存在或已被清理")。DELETE /api/v1/documents/{id}/permissions/entries/{entryId}/voucher清除指定授权条目的审批单凭证(授权条目本身保留)。校验口径与 12.3 相同。
404 RESOURCE_NOT_FOUND("该授权未上传审批单");DELETE_PERMISSION_VOUCHER(目标 PERMISSION)。成功返回:
{
"documentId": "1001",
"entryId": "7001"
}
以下两个接口适用于 MAIN、SUB_PLAN 和 ATTACHMENT,不再建立附件专用文件路径。
GET /api/v1/documents/{id}/previewapplication/pdf;application/pdf;415 PREVIEW_UNAVAILABLE;403 DOCUMENT_VIEW_FORBIDDEN;404 FILE_NOT_FOUND;503 PREVIEW_CONVERTER_UNAVAILABLE;504 PREVIEW_CONVERSION_TIMEOUT;502 PREVIEW_CONVERSION_FAILED。预览接口不新增路径;DOC/DOCX 预览 PDF 为可重新生成的衍生缓存,不属于正式档案;原始 DOC/DOCX 继续通过下载接口返回。
GET /api/v1/documents/{id}/downloadDOWNLOAD;仅有效 ADMIN 可以访问本章接口。USER返回 403 FORBIDDEN。
本阶段只支持 MAIN、SUB_PLAN和ATTACHMENT的回收站查询与单条恢复。不支持分类、用户或组织恢复,不支持批量恢复、物理删除、自动清理、保留期限、ACL或挂载关系独立恢复,也不移动文件到 recycle 目录。
GET /api/v1/recycle-bin/documents查询参数:
keyworddocumentType:MAIN|SUB_PLAN|ATTACHMENTcategoryId:字符串IDdeletedFrom:带 Z 的ISO 8601 UTC时间deletedTo:带 Z 的ISO 8601 UTC时间page,默认1pageSize,默认20、最大100sortField:仅允许 deletedAt|documentName|documentType|updatedAtsortDirection:asc|desc,默认 desc固定查询 isDeleted=true 且 deletedAt!=null。删除时间区间采用 [deletedFrom, deletedTo);允许只传一个边界,两者都传时必须满足 deletedFrom < deletedTo。不接受无时区、本地偏移时间或数字时间戳。
keyword只搜索文档名称、概述、searchText和原文件名,使用参数化包含匹配。空字符串按未传处理。
默认排序为 deletedAt desc, id desc;其他排序也使用 id作为同方向次级排序。返回 PageResult<RecycleBinDocumentSummary>,所有ID均为字符串。
POST /api/v1/recycle-bin/documents/{id}/restore请求体必须且只能包含:
{
"rowVersion": 3
}
rowVersion必须为非负整数。成功使用HTTP 200并返回 RestoreDocumentResult。
通用恢复校验:
rowVersion与当前删除记录一致;文件检查采用两阶段方案:事务前完成完整哈希和格式校验,保持文件句柄并记录文件状态;事务内锁定数据库记录后、提交前重新检查状态。变化时返回 RESTORE_FILE_CHANGED。数据库事务不能锁定磁盘文件,当前以应用独占管理UUID正式文件为前提。
MAIN恢复采用方案B:
deletedAt和updatedBy的级联删除ACL和挂载关系;expiresAt,已到期条目由读路径惰性判定失效,不构成恢复失败;documentCount、主案 childCount和attachmentCount;allowedActions由现有授权服务动态计算。SUB_PLAN恢复:
parentDocumentId和rootDocumentId必须指向同一有效MAIN;childCount。ATTACHMENT恢复后强制保持:
documentType=ATTACHMENT
securityLevel=PUBLIC
categoryId=null
parentDocumentId=null
rootDocumentId=null
附件不创建或恢复ACL,不自动恢复历史挂载,mountedPlanCount按当前有效关系统计。若异常存在指向已删除附件的有效挂载,返回关系冲突。
正式允许同类型文档同名、相同原文件名、分类下同名、同一主案下同名子方案、相同文件哈希以及不同文档使用相同哈希但各自保存独立文件路径。恢复冲突只处理有效ACL唯一冲突、有效挂载唯一冲突及主键或结构一致性冲突。
版本规则:
rowVersion;409 DATA_VERSION_CONFLICT及 details.currentRowVersion;成功审计使用 RESTORE_DOCUMENT,包括操作者、组织、目标快照、文档类型、版本前后值、原删除时间、恢复及跳过关系数量、父文档和分类ID、客户端IP、User-Agent和requestId。不得记录绝对路径、文件正文、数据库连接信息或文件哈希。最小范围内恢复失败不写业务审计,只写安全应用日志和统一错误响应。
固定锁顺序:
B8实现必须创建 0002_add_restore_audit_action,不得修改 0001_initial_schema。迁移必须:
sys_audit_log.action_type CHECK,加入 RESTORE_DOCUMENT;doc_document增加索引 (is_deleted, deleted_at, id);GET /api/v1/audit/logs查询参数:
keywordcreatedFromcreatedTouserIdorganizationIdactionTypeoperationResulttargetTypetargetIdpagepageSizesortDirection,默认 desc返回分页日志:
{
"id": "9001",
"username": "admin",
"realName": "系统管理员",
"organizationName": "机关",
"actionType": "DOWNLOAD_DOCUMENT",
"targetType": "DOCUMENT",
"targetId": "1001",
"targetName": "主案名称",
"operationResult": "SUCCESS",
"failureReason": null,
"clientIp": "127.0.0.1",
"requestId": "uuid",
"createdAt": "2026-07-23T03:00:00.000Z"
}
GET /api/v1/statistics/documents{
"planDocumentCount": 64,
"mainPlanCount": 21,
"subPlanCount": 43,
"attachmentCount": 18,
"todayViewerCount": 12
}
planDocumentCount = mainPlanCount + subPlanCount,不包含附件。
GET /api/v1/audit/statistics/trend参数:createdFrom、createdTo、granularity=DAY|WEEK|MONTH。
返回:
{
"items": [
{
"period": "2026-07-23",
"operationCount": 25
}
]
}
GET /api/v1/audit/statistics/actions参数:createdFrom、createdTo。
返回动作分布:
{
"items": [
{
"actionType": "VIEW_DOCUMENT",
"count": 20
}
]
}
GET /api/v1/audit/statistics/users参数:createdFrom、createdTo、limit,limit 默认10、最大100。
返回活跃用户:
{
"items": [
{
"userId": "1",
"realName": "系统管理员",
"organizationName": "机关",
"operationCount": 15
}
]
}
实现可以增加更细错误码,但不得改变以下语义:
INVALID_ARGUMENT
INVALID_CREDENTIALS
TOKEN_INVALID
TOKEN_EXPIRED
USER_DISABLED
AUTH_VERSION_MISMATCH
FORBIDDEN
SECURITY_LEVEL_FORBIDDEN
DOCUMENT_VIEW_FORBIDDEN
DOCUMENT_DOWNLOAD_FORBIDDEN
RESOURCE_NOT_FOUND
FILE_NOT_FOUND
UNSUPPORTED_FILE_TYPE
PREVIEW_UNAVAILABLE
PREVIEW_CONVERTER_UNAVAILABLE
PREVIEW_CONVERSION_TIMEOUT
PREVIEW_CONVERSION_FAILED
PAYLOAD_TOO_LARGE
BATCH_MANIFEST_MISMATCH
DATA_VERSION_CONFLICT
CATEGORY_IN_USE
MAIN_PLAN_HAS_CHILDREN
ATTACHMENT_IN_USE
ATTACHMENT_HAS_NO_ACL
SUB_PLAN_PERMISSION_INHERITED
DUPLICATE_PERMISSION_SUBJECT
INTERNAL_ERROR
DOCUMENT_NOT_DELETED
FILE_INTEGRITY_MISMATCH
FILE_PATH_INVALID
RESTORE_FILE_CHANGED
RESTORE_CATEGORY_INVALID
RESTORE_PARENT_INVALID
RESTORE_RELATION_CONFLICT
恢复错误码定义:
| code | HTTP | message | details |
|---|---|---|---|
DOCUMENT_NOT_DELETED |
409 | 文档当前不在回收站中 | documentId |
FILE_INTEGRITY_MISMATCH |
409 | 文档文件完整性校验失败 | reason=SIZE_MISMATCH\|HASH_MISMATCH\|TYPE_MISMATCH |
FILE_PATH_INVALID |
500 | 文档存储路径异常 | null |
RESTORE_FILE_CHANGED |
409 | 校验期间文件发生变化 | null |
RESTORE_CATEGORY_INVALID |
409 | 原方案分类无效,无法恢复 | categoryId、reason=NOT_FOUND\|DELETED\|DISABLED |
RESTORE_PARENT_INVALID |
409 | 原所属主案无效,无法恢复 | parentDocumentId、reason=NOT_FOUND\|DELETED\|WRONG_TYPE\|ROOT_MISMATCH |
RESTORE_RELATION_CONFLICT |
409 | 历史权限或挂载关系与当前有效关系冲突 | permissionConflictCount、bindingConflictCount |
(RESTORE_PERMISSION_INVALID 自 1.19 起废止:借阅语义下恢复不再校验"有效权限集合"形态,主体失效一律跳过计数,不再整体拒绝。)
错误响应不得泄露绝对路径、哈希、SQL、数据库连接信息或内部异常堆栈。
每个业务阶段实现接口时,必须同步更新 OpenAPI,并满足:
operationId 唯一;type: string、pattern: '^[0-9]+$';type: string、format: date-time;info.version升级为 1.1.0,同步回收站路径、DTO、错误码、RESTORE_DOCUMENT和迁移后的枚举。/api/v1/documents/{id}/restore不得进入OpenAPI。GET /api/v1/config/ui-dictionaries
ADMIN、USER均可访问。200,统一响应中的 data 为 UiDictionaries。401;启动期配置错误导致应用启动失败,运行期意外错误使用统一 500。DictionaryOption:
{
"code": "INTERNAL",
"label": "内部",
"sortNo": 20
}
UiDictionaries:
{
"version": "2026.1",
"locale": "zh-CN",
"dictionaries": {
"securityLevels": [
{
"code": "INTERNAL",
"label": "内部",
"sortNo": 20
}
]
}
}
dictionaries必须固定包含以下13个数组:roleCodes、allowedModules、documentTypes、documentStatuses、securityLevels、attachmentTypes、deptScopes、allowedActions、categoryTypes、enabledStatuses、auditResults、auditActions、auditTargets。(1.19 起移除 visibilityTypes 与 subjectTypes。)
每个数组按 sortNo、code稳定排序。外部字段使用 camelCase,sortNo为整数,code和label为字符串。响应不得包含配置路径、加载时间或内部异常;响应体 requestId 与 X-Request-Id保持一致,CORS沿用现有 /api/v1规则。
默认配置位于 backend/dms/resources/ui-dictionaries.zh-CN.json,可通过服务端环境变量 DMS_UI_DICTIONARY_CONFIG_PATH覆盖。配置在启动时严格校验并加载,修改后重启生效,不提供热更新。
前端:
code;401 统一清理Token并触发登录失效;allowedActions;后端:
snake_case 直接泄露到 JSON;执行会话不得直接修改本文件。接口需要变化时,应提交:
经统领会话确认后,提高契约版本,并同步功能契约、OpenAPI、前端类型和自动化测试。
版本1.1经统领会话批准,增加独立回收站查询和单条文档恢复,旧文档恢复路径继续永久废止。
版本1.2经统领会话批准,增加 GET /api/v1/config/ui-dictionaries 只读接口和严格启动配置规则;既有业务路径、枚举代码及数据库值不变,OpenAPI同步升级为 1.2.0。
版本1.3经统领会话批准,为方案、直属子方案和已挂载附件三个既有查询接口统一增加文档更新时间闭区间筛选;不新增路径、DTO、数据库字段或迁移,OpenAPI同步升级为 1.3.0。Office预览使用1.4或后续版本。
版本1.12经统领会话批准,新增 POST /api/v1/documents/{id}/re-extract 管理员接口;DocumentDetail 增加仅管理员可见的 contentExtractStatus/contentExtractError/contentExtractedAt 可选字段;PDF 正文提取合并段内软换行;数据库新增 doc_document.content_extract_error 列(迁移 0019_content_extract_error),OpenAPI同步升级为 1.12.0。
版本1.13经统领会话批准,新增公开接口 GET /api/v1/config/login-background(登录前无凭证访问):按序探测服务端运行目录下 background.jpg、background.png,命中即返回图片二进制(image/jpeg/image/png,含 nosniff 与 public, max-age=60 缓存头);均不存在时返回 404 FILE_NOT_FOUND 标准错误体,前端回退默认纯色背景。前端登录类界面(账号登录、门户跳转登录、认证状态页)自动探测该接口,有图时拉伸铺满(cover 居中不重复,保留纯色兜底),同一会话只探测一次。无数据库变更,OpenAPI同步升级为 1.13.0。
版本1.14经统领会话批准:①附件类型扩展四值ACCOMPANYING_PLAN(配套方案,UI字典attachmentTypes居首),原SUB_PLAN文档停止新建(创建请求返回400 INVALID_ARGUMENT"配套方案已并入共享资料库,请以附件类型ACCOMPANYING_PLAN上传"),存量子案由迁移0020_sub_plan_to_attachment转为该类型附件(保留密级、置空父/根/分类、父子关系转为挂载关系);②主案allowedActions对ADMIN恒含UNBIND_ATTACHMENT(与BIND对称,移除"存在附件挂载"前置);③附件shape约束放宽(security_level不再强制PUBLIC),附件详情/预览/下载增加密级前置(密级不足返回403 SECURITY_LEVEL_FORBIDDEN,任何角色含ADMIN一致);④共享资料库条目均携带attachmentType四类之一,配套方案Tab的上传/挂载/解绑/排序与其余三类附件同构。OpenAPI同步升级为1.14.0。
版本1.15经统领会话批准,机构维护增强:PUT /api/v1/organizations/{id}载荷新增必填orgCode(机构编码可编辑),保存时查重(排除自身、仅比对未删除机构),冲突返回409并提示"机构编码已存在";创建语义不变。OpenAPI同步升级为1.15.0。
版本1.16经统领会话批准,移除角色"可访问部门"维度:①GET/PUT /api/v1/roles/{id}/departments两个接口下线,sys_role_dept表删除(迁移0021_remove_assigned_scope / Flyway V6__remove_assigned_scope);②DeptScope枚举收敛为OWN/OTHER,"其他部门"涵盖所属部门子树之外的全部部门(含无归属文档);③权限矩阵由每角色15行(5密级×3部门范围)收敛为10行(5密级×2部门范围),存量dept_scope='ASSIGNED'矩阵行物理删除;④UI字典deptScopes移除ASSIGNED选项。OpenAPI同步升级为1.16.0。
版本1.17经统领会话批准,管理类权限矩阵化(分类/机构/用户编辑权限入角色):①sys_role新增manage_category_edit/manage_org_edit/manage_user_edit三管理位与managed_org_id管理范围锚点(NULL=全局),角色创建/编辑接口(POST/PUT /api/v1/roles)载荷新增对应四个字段;②判定读"生效角色"(用户显式角色活跃且启用时优先,否则回退默认角色),仅ADMIN类别生效:分类编辑全局生效,机构/用户编辑受锚点部门及其下级子树(沿启用边BFS)约束,无权返回403 FORBIDDEN("无分类/机构/用户编辑权限"或"目标超出管理范围");锚点接受特殊值"SELF"(落库哨兵0,0非合法机构ID),判定时锚点跟随各用户自身organization_id(即用户身居哪个部门就管该部门子树,用户无部门时管理范围为空集),角色与用户接口序列化时0还原为"SELF";③内置admin账号恒为全权超管,不受三管理位与范围约束,登录与用户详情的permXxx/managedOrgId四字段对超管恒返回全开/全局(不因其无显式角色回退默认角色而归零);④迁移0023_admin_permissions(Flyway V8)先落用户级过渡列并初始化存量(ADMIN默认三权限开启、锚点=所在部门),0024_manage_permission_matrix(Flyway V9)将存量用户级配置收敛上收到生效角色(多组占用时克隆角色追加-M{n}后缀并回填),随后删除sys_user用户级四列;⑤UserSummary新增permCategoryEdit/permOrgEdit/permUserEdit/managedOrgId管理位展示(四字段值均取自生效角色,字段名沿用过渡期命名),用户创建/更新接口不再接受管理位字段(载荷携带返回400 INVALID_ARGUMENT)。OpenAPI同步升级为1.17.0。
版本1.18经统领会话批准,新增跨页调整顺序两个接口(拖动重排仅限当前页内,跨页移动由行级"移动"入口承载):①PUT /api/v1/documents/{documentId}/move——管理员/受权用户将主案或配套方案移动到浏览范围内指定位置,载荷为{targetPosition}与{target:"TOP"|"BOTTOM"}二选一(非法组合返回400 INVALID_ARGUMENT),主案可携带可选scopeCategoryId(分类子树语义,配套方案携带时返回400);②PUT /api/v1/main-plans/{mainPlanId}/attachments/{bindingId}/move——移动主案挂载附件到页签类型范围内指定位置,载荷必填mainPlanRowVersion(乐观锁,过期返回409 DATA_VERSION_CONFLICT),可选scopeAttachmentType限定附件类型范围。位置为范围内1-based序号并自动收敛到[1,范围内总数];重排采用"可见子序列重排+槽位回填",范围外条目保持原相对顺序;目标位与当前位相同时为无操作(不写库、不审计),响应文档移动{position,total}、挂载移动{position,total,mainPlanRowVersion}(有实际移动时主案rowVersion加1并回传)。权限校验与拖动重排一致(文档EDIT / 主案BIND_ATTACHMENT,设密主案同过密码锁);文档不在指定分类/类型范围内返回409 DOCUMENT_NOT_IN_MOVE_SCOPE(新增错误码)。审计动作新增MOVE_DOCUMENT(目标DOCUMENT)与MOVE_ATTACHMENT(目标ATTACHMENT_BINDING),UI字典auditActions同步两词条;sys_audit_log的action_type CHECK白名单同步扩展至38项(迁移0025_move_audit_actions / Flyway V10__move_audit_actions,达梦下约束名为大写CK_SYS_AUDIT_LOG_AUDIT_ACTION_TYPE,探测删除后重建)。前端管理页主体方案表与挂载附件表新增"移动"行操作及位置对话框(数字目标+置顶/末尾快捷,成功后跳转目标页)。OpenAPI同步升级为1.18.0。
版本1.19经统领会话批准,权限模块整体改为借阅授权语义:①授权界面三种可见范围(ALL_AUTHENTICATED/ORGANIZATION/CUSTOM)全部下线,只保留面向用户的限时授权——操作位收敛为canView/canDownload两项,expiresAt必选且最长7天(Z结尾UTC,服务端60秒容差,到期由读路径惰性判定失效);②GET/PUT /api/v1/documents/{id}/permissions载荷随之收敛(详见第12章),保存请求字段固定为documentRowVersion+entries(每条仅subjectId/canView/canDownload/expiresAt),保存不触碰审批单凭证;③每条授权可选挂一份PDF审批单凭证,新增POST/GET/DELETE /api/v1/documents/{id}/permissions/entries/{entryId}/voucher三个接口(上传为替换语义,旧文件在DB事务成功后清理;仅主案可操作,校验口径与授权保存一致),审计动作新增UPLOAD_PERMISSION_VOUCHER/DELETE_PERMISSION_VOUCHER(目标PERMISSION,UI字典auditActions同步两词条,sys_audit_log.action_type CHECK白名单扩至40项);④DocumentSummary的visibilityType+visibilitySummary改为borrowSummary(生效借阅人姓名、连接,无生效借阅为未配置,无查看权限列表行为-),DocumentDetail.permissionSummary改为{borrowerCount, inheritedFromMainPlan}(与摘要同口径统计生效借阅人数);⑤GET /api/v1/documents移除visibilityType查询参数;⑥数据库迁移0026_borrow_permission(FlywayV11__borrow_permission,双引擎幂等互冲):doc_permission物理删除全部ORG主体行,删列subject_type/can_edit/can_manage_permission/can_delete,唯一约束收敛为(document_id, subject_id, active_marker),新增expires_at(NOT NULL,1970哨兵fail-safe,存量活跃USER行初始化为迁移时刻+7天)与凭证四列voucher_file_name/voucher_storage_path/voucher_file_size/voucher_uploaded_at;doc_document删除visibility_type列(shape约束重建为v3),存量"全员可见/组织可见"文档回退角色权限矩阵控制;⑦恢复语义调整:授权还原仅面向用户主体(失效主体跳过计数),保留原expiresAt(已到期条目不构成失败),错误码RESTORE_PERMISSION_INVALID废止;⑧UI字典移除visibilityTypes/subjectTypes两组(13组),权限主体枚举(3.8节)整体废止。前端授权弹窗改为借阅语义(借阅人/查看/下载/到期时间/审批单列,datetime-local分钟粒度,max=当前+7天,到期条目标记"已到期",凭证行级上传/下载/替换/清除)。OpenAPI同步升级为1.19.0`。
版本1.20经统领会话批准,新增界面主题(账号级漫游):①新增PUT /api/v1/users/me/ui-theme——本人界面主题偏好,任意登录用户可改自己、无需管理权限,载荷为{uiTheme}白名单blue/red/green(非法值返回400 INVALID_ARGUMENT,载荷未知字段严格拒绝),成功返回最新UserResponse;主题为纯视觉偏好:不校验rowVersion(不干扰并发编辑乐观锁)、不bump authVersion(不使Token失效下线会话)、不写审计日志;②UserSummary(登录、users/me、用户管理列表与详情)新增必返字段uiTheme,存量与缺省为blue;③前端登录/门户设密/会话恢复三入口以后端账号偏好对齐本地主题(后端为权威源,本地localStorage仅作防闪烁缓存——index.html内联脚本在首帧前按缓存设置html[data-theme],登录后被账号值覆盖);④数据库迁移0027_ui_theme(Flyway V12__ui_theme,双引擎幂等互冲):sys_user加ui_theme VARCHAR(12) NOT NULL DEFAULT 'blue',CHECK白名单blue/red/green(convention渲染名ck_sys_user_user_ui_theme;达梦约束名为大写,两端探测均大小写不敏感比较、DROP用库中实际名);达梦下Flyway默认关闭(DM不在其支持列表),另附等价手工脚本db/migration-dm/V12__ui_theme.sql(DMSQL匿名块+EXECUTE IMMEDIATE+USER_TAB_COLUMNS/USER_CONSTRAINTS探测,与db/migration平级目录不参与Flyway扫描);⑤前端主题基础设施:theme.css新增--dms-deep/--dms-bright/--dms-canvas及--dms-*-rgb透明度族语义变量与--chart-6,html[data-theme="red"|"green"]覆盖块整体换肤(background/primary/chart/sidebar等全套设计token),全站硬编码主色(含App.tsx色板常量与各组件rgba(…)透明色族约220处)变量化迁移至CSS变量;例外不迁移:密级徽章、安全水印、审计扇形多彩色板、destructive警示色(内容语义色不随主题变化);⑥前端顶栏新增主题切换入口(调色板下拉,三色样高亮当前项),切换本地立即生效并异步PUT同步账号,保存失败提示但本地不回滚。OpenAPI同步升级为1.20.0。
RoleCode仅为ADMIN、USER。四个审计查询接口与文档统计接口均要求ADMIN。历史AUDITOR不能登录,旧Token必须因authVersion或用户状态校验返回401。GET /api/v1/documents新增可选布尔参数includeDescendants。非法布尔值返回400 INVALID_ARGUMENT。仅在传入categoryId时true表示包含该分类全部有效后代;缺省或false为精确分类;未传分类时查询当前用户全部可见MAIN。documentName、documentType=MAIN、categoryId、securityLevel、summary、tags。SUB_PLAN创建仅接受documentName、documentType=SUB_PLAN、parentDocumentId、securityLevel、summary、tags。未知字段严格拒绝。单文件和批量导入一致。documentName、summary、categoryId、securityLevel、tags、rowVersion;SUB_PLAN编辑不含categoryId。两者均不接受status或visibilityType。409 CATEGORY_NOT_LEAF,details至少包含字符串categoryId和整数childCategoryCount。GET /api/v1/attachments对所有登录用户开放且全量可见(支撑全局检索按标题命中全部附件);普通用户返回的allowedActions为空集,且不返回description与contentSnippet;上传、编辑、删除等写操作仍仅ADMIN。附件详情、统一preview/download路径对USER执行“至少一个有效挂载主案及对应VIEW/DOWNLOAD权限”检查。GET /api/v1/documents/{id}/preview的200响应支持application/pdf与application/vnd.openxmlformats-officedocument.wordprocessingml.document,返回原文件并携带inline Content-Disposition、X-Content-Type-Options、Cache-Control和X-Request-Id。DOC、XLS、XLSX返回统一JSON 415 PREVIEW_UNAVAILABLE。info.version为1.4.0,迁移为0003_q3_business_alignment_and_content_search。