契约版本:1.33 状态:已确认,登录图片验证码基线 外部字段风格:
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-DMS-Reauth-Token: <reauthToken>(由 POST /api/v1/auth/reauth 重输登录密码换取,5 分钟有效,见 6.3)。缺失、无效、过期、非本人或 authVersion 不符返回 403 REAUTH_REQUIRED。覆盖接口:
PUT /api/v1/users/{id}:仅当 securityLevel、roleCode、roleId、status 实际发生变化时强制(纯改名/调部门不要求);PUT /api/v1/documents/{id}/permissions(借阅授权保存,含授予与撤销);POST /api/v1/documents/{id}/permissions/entries/{entryId}/voucher 与 DELETE 同路径(审批单凭证上传与清除);PUT /api/v1/roles/{id}/permissions(角色权限矩阵保存)。客户端未传 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无需认证。
请求(验证码开启时,见 6.5 与环境变量 DMS_CAPTCHA_ENABLED):
{
"username": "admin",
"password": "password",
"keepSignedIn": true,
"captchaId": "uuid",
"captchaCode": "A3BK"
}
验证码关闭时请求体保持三键(username/password/keepSignedIn,不得携带验证码字段);开启时必须且只能包含上述五键。
成功:
{
"code": "OK",
"message": "success",
"data": {
"accessToken": "jwt",
"tokenType": "Bearer",
"expiresIn": 3600,
"idleLogoutMinutes": 5,
"user": {}
},
"requestId": "uuid"
}
user 使用 UserSummary。用户名或密码错误统一返回 401 INVALID_CREDENTIALS,不得泄露用户是否存在。
验证码校验(契约1.32):
captchaId 校验后即作废,重放返回 400 INVALID_CAPTCHA("验证码错误或已过期");captchaId 统一返回 400 INVALID_CAPTCHA,不区分具体原因。Token有效期:
keepSignedIn=false:expiresIn 取环境变量 DMS_JWT_EXPIRES_SECONDS(双版同名,缺省 3600 即 1 小时);keepSignedIn=true:expiresIn=604800(7 天,固定)。第一阶段不签发刷新令牌。
空闲自动登出(契约1.29新增,门户设密完成响应同):
idleLogoutMinutes 取环境变量 DMS_IDLE_LOGOUT_MINUTES(双版同名,缺省 5),0 表示关闭;keepSignedIn 存入 localStorage 或 sessionStorage,会话恢复后沿用。POST /api/v1/auth/logout请求体为空。成功后递增当前用户 authVersion,使该用户全部旧Token失效。
POST /api/v1/auth/reauth敏感操作二次认证(契约1.21新增)。登录态下重输登录密码,换取短时 reauth 令牌。
请求体(严格单键):
{
"password": "login-password"
}
成功:
{
"code": "OK",
"message": "success",
"data": {
"reauthToken": "jwt",
"expiresIn": 300
},
"requestId": "uuid"
}
规则:
sub/type=reauth/authVersion/iat/exp/jti/iss,固定 5 分钟有效,仅可用于 X-DMS-Reauth-Token 请求头,不能作为 Authorization: Bearer 访问令牌(缺 username/roleCode 声明,普通访问校验必然拒绝);userId 与 authVersion:authVersion 变化(改角色/停用/登出/改密)后立即失效;401 INVALID_CREDENTIALS;不写审计、不计入登录失败锁定;expiresIn 提前过期。GET /api/v1/users/me返回当前 UserSummary。
GET /api/v1/auth/captcha登录图片验证码签发(契约1.32,安全需求4)。无需认证。
成功(开关开启):
{
"code": "OK",
"message": "success",
"data": {
"enabled": true,
"captchaId": "uuid-hex",
"image": "data:image/png;base64,..."
},
"requestId": "uuid"
}
开关关闭时 data 仅含 {"enabled": false},登录页隐藏验证码输入、登录请求体保持三键。
规则:
DMS_CAPTCHA_ENABLED(双版同名,接受 1/true/yes/on,缺省 true 即默认启用);img 标签 src 直接使用),4 位字符(剔除易混淆 0O1I),忽略大小写校验;Cache-Control: no-store,前端不得缓存;验证码获取失败静默隐藏输入框不阻塞登录流程(后端可能未开启)。GET /api/v1/organizations/tree查询参数:
keyword:可选,组织名称或编码;status:可选,按状态过滤;不传时返回全部机构(含停用,契约1.29),停用节点仍保留其祖先链以便展示树形层级。响应按请求者生效管理范围裁剪(契约1.33):锚点管理员仅返回管理范围子树内的机构(森林形式,根从锚点机构起,范围外机构与其祖先链不出现,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)
敏感操作:请求必须携带有效 X-DMS-Reauth-Token(见 2.1 与 6.3),否则 403 REAUTH_REQUIRED。
只允许对 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 + 密级 + 密码锁)。敏感操作:需 X-DMS-Reauth-Token(403 REAUTH_REQUIRED)。
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 相同。敏感操作:需 X-DMS-Reauth-Token(403 REAUTH_REQUIRED)。
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。
版本1.21经统领会话批准,安全加固三项:①访问令牌有效期默认由7200秒收敛为3600秒(1小时)并配置化——环境变量DMS_JWT_EXPIRES_SECONDS(双版同名,非法值启动即失败),keepSignedIn=true的7天保持不变;②敏感操作二次认证——新增POST /api/v1/auth/reauth(登录态重输登录密码换取5分钟reauth令牌,claims含type=reauth并绑定sub与authVersion,不可冒充访问令牌,authVersion变化即失效),敏感接口携带X-DMS-Reauth-Token请求头校验,缺失/无效/过期/非本人/版本不符一律返回新增错误码403 REAUTH_REQUIRED("敏感操作需要二次认证,请重新输入登录密码");覆盖接口为:PUT /api/v1/users/{id}(仅当securityLevel/roleCode/roleId/status实际变化时强制)、PUT /api/v1/documents/{id}/permissions(借阅授权保存含授予与撤销)、POST与DELETE /api/v1/documents/{id}/permissions/entries/{entryId}/voucher(审批单凭证上传与清除)、PUT /api/v1/roles/{id}/permissions(角色权限矩阵保存);前端在对应操作提交前弹出登录密码确认框,令牌内存缓存5分钟内免重复输入;③应用层信封加密——新增环境变量DMS_STORAGE_KEK(base64编码32字节AES-256密钥),配置后original/新落盘文件按DMSE+kekVersion(1B)+包裹DEK长度(4B)+KEK包裹的DEK+密文格式以AES-256-GCM加密(DEK每文件随机32B、nonce 12B),读路径按DMSE头自动识别密文/明文实现明文兼容,未配置KEK时维持明文落盘;存量文件不回迁加密,预览转换/正文提取等读路径经统一解密入口(密文解出临时明文、用后清理)。OpenAPI同步升级为1.21.0。
版本1.22经统领会话批准,数据备份定版:①备份管理5接口(GET/PUT /api/v1/backup/config、POST /api/v1/backup/jobs(202受理)、GET /api/v1/backup/jobs/current(进度快照)、POST /api/v1/backup/jobs/current/cancel、GET /api/v1/backup/jobs(历史清单)),配置含目标模式(LOCAL本机目录/RSYNC守护进程rsync://user@host:873/module/专用协议推送,含密码认证与--delete镜像清理)、定时开关与时刻、全量间隔天数、保留链数;②全量/增量链式备份覆盖达梦dexp逻辑导出、original/preview/extracted/quarantine/recycle/voucher六个存储目录与.env配置副本,增量基于最近全量差异传输;③审计动作新增BACKUP_CREATE(目标BACKUP,迁移0028_backup_audit/Flyway V13,含达梦适配版);④离线恢复CLI(Pythondms-server restore-backup/Java--restore-backup)按清单恢复存储文件,.env存为.env.restored不覆盖当前配置。OpenAPI同步升级为1.22.0。
版本1.23经统领会话批准,系统管理:①角色新增两管理位manage_system/manage_audit_log(sys_role加列,迁移0029_system_management/Flyway V14双引擎幂等,含达梦适配版),角色创建/编辑载荷与回显字段名为manageSystemManage/manageAuditLog;UserSummary新增permSystemManage/permAuditLog(内置admin恒true);②模块推导——AUDIT_LOG改由manage_audit_log推导、新增SYSTEM_MANAGEMENT由manage_system推导(内置admin恒可见),浏览/后台管理/机构用户管理推导不变;③鉴权调整——备份6接口与system/**新接口改系统管理位判定、审计5接口与system/logs/download改日志审计位判定(原ADMIN硬判定废止,admin恒过);④新增/api/v1/system/**共12接口:metrics/current(实时CPU/内存/交换/磁盘/开机时间)、metrics/history?hours=(1~720缺省24,采样表sys_metric_sample30秒间隔保留7天)、process(进程CPU/内存/线程数/运行时长,pythonVersion字段Java版返回JDK版本)、storage-usage(七存储子目录遍历统计)、converter(LibreOffice存活/并发占用/累计成功失败计数,内存态重启清零)、health(数据库/存储可写/转换器/备份目标/信封加密五项体检)、info(契约版本/后端类型PYTHON|JAVA/运行时/OS/数据库类型/启动时间/关键路径)、runtime-config(15项运行参数脱敏总览,密钥类仅显示已配置/未配置)、logs?lines=&level=(运行日志尾部1~5000行缺省500,按[LEVEL]标记过滤)、logs/download(原始日志text/plain附件)、maintenance/preview-cache(POST清空预览缓存,审计PREVIEW_CACHE_CLEAR)、users/{user_id}/force-offline(POST提升authVersion强制下线,自保护拒绝本人,审计FORCE_OFFLINE);⑤新增POST /api/v1/backup/test-target(备份目标连通性探测,审计BACKUP_TEST_TARGET,探测失败返回{ok:false,error}不报错);⑥新配置DMS_LOG_FILE(运行日志文件,缺省logs/dms-server.log,10MB滚动×5,OFF/NONE关闭)、DMS_METRIC_INTERVAL_SECONDS(缺省30,下限10)、DMS_METRIC_RETENTION_DAYS(缺省7,下限1);⑦Python版资源采集依赖psutil(缺失时监控接口返回503 METRIC_UNAVAILABLE),Java版用JDK内建MXBean零依赖;审计动作CHECK白名单扩至42项、目标类型白名单扩至12项(新增SYSTEM)。OpenAPI同步升级为1.23.0。
版本1.24经统领会话批准,门户设密自选机构:①POST /api/v1/auth/portal/verify的SET_PASSWORD阶段响应新增organizations(本系统启用机构下拉,元素{id,orgCode,orgName,orgPath},id为字符串,按orgPath稳定排序)与suggestedOrganizationId(按对端orgName推断的预选机构ID字符串,推断不出为null)——本系统按需同步用户、不自动同步机构树,两系统机构树可能不一致,由用户设密时自选所在机构;②POST /api/v1/auth/portal/setup-password载荷新增可选organizationId(字符串,须指向有效且已启用机构,非法返回400 INVALID_ARGUMENT"organizationId必须指向有效且已启用的机构",非字符串/空串拒绝),设密成功时用户机构同步更新为所选机构并记入LOGIN审计detail(仅实际变化时记录organizationId);缺省该字段(旧客户端兼容)不改变用户机构;③前端设密界面在机构列表非空时渲染必选"所在机构"下拉(预选suggestedOrganizationId,未选提交提示"请选择所在机构"),列表为空时跳过不强制;④Python与Java双版实现逐条对齐(Java侧updatePortalPassword单UPDATE合并机构列,保持单次row_version+1与Python ORM单写一致);⑤无数据库结构变更、无新增路径。OpenAPI同步升级为1.24.0。
版本1.25经统领会话批准,本地单点登录代理(仅Java版):①新增POST /api/v1/auth/portal/proxy-verify(无认证、无请求体)——令牌取自部署机本机单点登录代理(Java后端经sso-sdk:UserClient.getInstance(host,port).getToken()取token,UserService.getInstance(host,port).getUserBasicInfo(token)以token换用户基本信息userId/username/realName,对端不提供机构字段恒为null),随后的按需同步建档与双阶段判定和门户跳转验签完全共用同一实现,响应结构与portal/verify逐字段一致(LOGIN/SET_PASSWORD双阶段,首登返回一次性设密票据与机构自选列表);②仅Java版后端提供本接口,Python版不实现(请求返回404)、功能未启用返回400 INVALID_ARGUMENT"单点登录代理未启用",前端在未登录且地址栏无门户query时打开页面自动静默探测一次,探测失败(404/400/代理不可达/令牌无效)静默回落账号密码登录页,成功则进入与门户跳转一致的双阶段界面;③新配置项(仅Java版消费):DMS_SSO_PROXY_ENABLED(开关,缺省false)、DMS_SSO_PROXY_HOST/DMS_SSO_PROXY_PORT(代理地址,缺省127.0.0.1:8080)、DMS_SSO_USER_SERVICE_HOST/DMS_SSO_USER_SERVICE_PORT(用户服务地址,缺省127.0.0.1:8800)、DMS_SSO_TIMEOUT_SECONDS(缺省10);sso-sdk以system scope依赖引入(backend-java/lib/sso-sdk-0.1.0.jar,打包经includeSystemScope进入fat jar),当前为mock实现(源码见仓库mock-sso-sdk/,配套mock代理tools/mock-sso-proxy/mock_sso_proxy.py),真实SDK到货后替换jar即可,SsoProxyGateway为唯一封装点;④审计沿用门户链路(CREATE_USER/EDIT_USER/LOGIN,detail含portal:true),设密机构自选沿用1.24契约;⑤无数据库结构变更。OpenAPI同步升级为1.25.0。
版本1.26经统领会话批准,监控信息展示开关:①新增配置DMS_SYSTEM_MONITOR_ENABLED(双版同名,接受1/true/yes/on大小写不敏感,缺省false);②GET /api/v1/system/info响应新增必填布尔字段monitoringEnabled(SystemInfoData同步加列),双端实现逐条对齐;③为false时前端系统管理页隐藏"维护操作"区块之上的全部监控信息(系统信息、应用进程、实时资源、资源采样趋势、存储目录用量、文档转换器、运行参数总览),并跳过对应监控接口请求,仅保留操作条、维护操作与数据备份区域;接口本身不下线、不追加权限判定;④GET /api/v1/system/runtime-config总览新增"系统/监控信息展示"条目回显当前值(启用/停用);⑤无数据库结构变更、无新增路径。OpenAPI同步升级为1.26.0。
版本1.27经统领会话批准,列表拖动重排:①新增三个重排接口——PUT /api/v1/organizations/reorder(载荷{parentId,orderedIds},parentId为null表示顶层机构排序,否则为该父机构直接子级排序,上级不存在返回404、机构超出管理范围返回403)、PUT /api/v1/users/reorder(载荷{orderedIds})、PUT /api/v1/roles/reorder(载荷{orderedIds}),orderedIds为拖动后的目标顺序(正整数字符串ID数组、无重复、至少2个,否则400 INVALID_ARGUMENT;含列表外的无效ID同样400);②重排采用槽位置换语义:被提交ID按原槽位次序排好依次填入目标顺序,未提交行相对次序不变(分页部分提交安全);变更行按步长10重编号sort_no且row_version加1,sort_no无实际变化的行不写库,响应统一为{reorderedCount}(实际写库行数);③权限——机构/用户reorder要求ADMIN并分别校验机构编辑位/用户编辑位(机构受管理范围锚点约束),角色reorder要求ADMIN且角色编辑位,与各自编辑接口一致;④审计动作新增REORDER_ORGANIZATION/REORDER_USER/REORDER_ROLE(UI字典auditActions同步三词条,sys_audit_log.action_type CHECK白名单扩至45项,detail含orderedIds/reorderedCount,机构另含scope=parentId或"ROOT");⑤数据库迁移0031_user_sort_no(Flyway V16__user_sort_no,双引擎幂等互冲,含达梦适配版):sys_user加sort_no INT NOT NULL DEFAULT 0(机构/角色已有同名列),用户列表排序由real_name,id改为sort_no,id,存量用户按id序展示;⑥前端机构树同级拖拽、用户列表与角色列表行拖拽排序接入三个接口。OpenAPI同步升级为1.27.0。
版本1.28,IP条目范围形式:①IP条目ipValue在精确IP与无主机位CIDR网段之外新增起止IP范围形式(如192.168.1.1-192.168.1.254,连接符为半角减号,仅允许一个);②两端各自按精确IP规则解析(IPv4拒绝前导零与非法段、IPv6字面量),要求两端同为IPv4或同为IPv6(否则400 INVALID_ARGUMENT「IP范围两端必须同为IPv4或IPv6:原文本」),起始不得大于结束(否则400「IP范围起始不能大于结束:原文本」),规范化存储为「规范化起文本-规范化止文本」;多余连字符、空端点等非法格式统一提示「IP地址、网段或IP范围格式非法:原文本」(原「IP地址或网段格式非法」文案同步扩展);③登录IP校验同步支持:来源地址与范围同版本且落在任一分配条目的[起,止]闭区间(含两端边界)即放行;④无数据库结构变更(复用ip_value文本列,规范化结果超过64字符上限时按长度超限400拒绝),存量精确IP/CIDR条目行为不变;⑤前端防手误确认:录入/编辑起止范围时,若两端前三段(IPv4 C类网段)不一致,提交前弹窗二次确认并提示匹配IP总数(止−起+1,千分位展示),取消则不提交;纯前端交互,后端仍接受跨网段范围,无接口变更。OpenAPI同步升级为1.28.0。
版本1.29,页面空闲自动登出与机构树默认语义:①登录(POST /api/v1/auth/login)与门户设密完成(POST /api/v1/auth/portal/setup-password)响应data新增必填整数字段idleLogoutMinutes(≥0)——取环境变量DMS_IDLE_LOGOUT_MINUTES(双版同名,非负整数、非法值启动即失败,缺省5),0表示关闭;reauth响应语义不同不携带;②前端登录/设密/会话恢复后将该值随令牌按keepSignedIn同策略持久化(localStorage或sessionStorage,退出登录清除),页面监测鼠标移动/按键/滚动/触摸/滚轮/点击活动,连续无活动达到配置分钟数后自动调用logout退出并固定提示「长时间未操作,已自动退出登录」(15秒周期检查,触发一次后不再重复,重新登录重新计时);③GET /api/v1/organizations/tree查询参数status缺省语义修正——不传时返回全部机构(含停用)(原实现缺省仅返回启用机构导致停用机构从管理列表消失),传ENABLED/DISABLED仍按值过滤,过滤时停用节点保留其祖先链;④无数据库结构变更、无新增路径。OpenAPI同步升级为1.29.0。
版本1.30,用户级管理范围:①sys_user新增managed_org_id三态列(迁移0032_user_managed_org,Flyway V17__user_managed_org,双引擎幂等互冲安全、含达梦适配版):NULL=全局、哨兵0=SELF(跟随本人所在机构,存量回填0、换机构自动跟随)、机构ID=固定锚点(含子树,沿启用边BFS);②用户创建(POST /api/v1/users)与更新(PUT /api/v1/users/{id})载荷新增可选字段managedOrgId(null/空串=全局、"SELF"=跟随所在机构、机构ID须指向未删除机构否则400):create省略时落SELF(哨兵0),update省略时保持原值不变(显式null=设为全局);该字段调整不改变认证身份——不触发reauth、不递增authVersion,仅随常规编辑递增rowVersion并计入EDIT_USER审计before/after;③UserSummary.managedOrgId语义改回用户级三态(机构ID/"SELF"/null,内置admin恒null),不再取生效角色锚点;④范围计算规则——实际生效范围以生效角色锚点为准;角色锚点为"SELF"(哨兵0)时回落用户级设置:用户NULL=全局、用户SELF=其所在机构子树(无部门则空集)、用户机构ID=该锚点子树;内置admin恒全局。管理范围对机构/用户编辑与用户列表收敛生效,分类/角色编辑不受锚点约束;用户列表口径修订——不传organizationId时仍静默收敛到管理范围子树(范围空集返回空页),显式请求的organizationId(含后代展开)与管理范围无交集时返回403 FORBIDDEN("无权限查看该机构的用户"),不再静默交集返回空页,避免前端误显示"该机构无用户"。⑤移动权限口径修订(修订1.18)——只有对文档有EDIT权限的用户才可"移动"文档:主案/子案移动与拖动重排维持文档自身EDIT判定不变;主案挂载附件的移动(PUT /main-plans/{id}/attachments/{bindingId}/move)与拖动重排由主案BIND_ATTACHMENT改判主案EDIT(调整主案挂载区顺序属主案编辑范畴,与挂载/解挂的BIND_ATTACHMENT/UNBIND_ATTACHMENT解耦),无权返回403 FORBIDDEN;前端管理页主体方案表"移动"按钮按行级allowedActions无EDIT置灰(悬停提示原因),挂载附件表"移动"与拖动按所选主案EDIT表级控制;⑥用户编辑防提权——POST /api/v1/users与PUT /api/v1/users/{id}新增目标生效范围约束:仅当目标roleCode=ADMIN且其生效角色具备机构/用户编辑位(manageOrgEdit/manageUserEdit 任一)时校验,目标最终生效管理范围(按「角色锚点优先,角色SELF回落用户级三态」解析)不得超出操作者管理范围——解析为全局(角色锚点或用户级为NULL)或范围外机构返回403 FORBIDDEN("目标超出管理范围");无管理位角色(含默认角色)与USER类别目标不受限,update未提交managedOrgId时按保持原值判定,校验先于二次认证执行(换角色场景先报越权而非REAUTH_REQUIRED);内置admin作为操作者恒全局不受限。OpenAPI同步升级为1.30.0。
版本1.31,矩阵上传位与主题背景图:①角色方案权限矩阵新增上传位——RolePermissionRow布尔位扩为8个(canView/canUpload/canDownload/canEdit/canDelete/canConfigPermission/canBindAttachment/canUnbindAttachment,canUpload位列canView之后),canUpload仅OWN行有意义、OTHER行恒false(提交OTHER行为true返回400 INVALID_ARGUMENT「上传权限仅可设置本部门方案」);迁移0033_role_upload_permission(Flyway V18__role_upload_permission,双引擎幂等互冲,含达梦适配版)为sys_role_permission加can_upload列(存量行回填false,需上传的角色由管理员显式开启);②上传判定——POST /api/v1/documents与POST /api/v1/documents/batch-import按矩阵上传位校验:内置admin恒可,其余用户按方案归属部门取生效角色对应行(本部门OWN行、他部门OTHER行恒拒)的canUpload,无权返回403 FORBIDDEN;③UserSummary新增必填布尔字段permUpload(生效角色矩阵任一OWN行canUpload为true即true,内置admin恒true)——该位为前端入口门控(上传/批量导入按钮显隐),服务端仍按②逐文档精确判定;前端界面矩阵列更名「权限矩阵」→「方案权限」,canBindAttachment/canUnbindAttachment界面合并为单一「挂载」开关(提交两列同值,接口字段与语义不变);④登录IP限制豁免收窄(修订1.10)——机构分配IP条目后仅内置admin账号豁免,原ADMIN类别整体豁免废止,其余管理员与普通用户同样受所在机构IP限制;⑤角色锚点简化——角色维护界面移除「机构及用户管理范围」下拉、恒按「跟随用户设置」(SELF)提交,迁移0034_role_anchor_self(Flyway V19__role_anchor_self,双引擎幂等,含达梦适配版)将存量角色锚点全部回填为SELF(哨兵0),生效管理范围统一按1.30④用户级三态解析;⑥登录背景图按主题——GET /api/v1/config/login-background新增可选查询参数theme(枚举blue/red/green,白名单外取值按未提供处理),提供时优先返回运行目录下background-{theme}.jpg/png(jpg优先),主题图缺失回落通用background.jpg/png;前端按html[data-theme]当前主题请求并按主题独立缓存。OpenAPI同步升级为1.31.0。
版本1.32,登录图片验证码(安全需求4):①新增公开接口GET /api/v1/auth/captcha——开关开启时返回{enabled: true, captchaId, image}(image为data:image/png;base64前缀的132×44 PNG,4位字符剔除易混淆0O1I,120秒有效),关闭时仅返回{enabled: false};②开关取环境变量DMS_CAPTCHA_ENABLED(双版同名,接受1/true/yes/on,缺省true即默认启用);③开启时POST /api/v1/auth/login请求体扩为严格五键(追加captchaId/captchaCode,忽略大小写与首尾空白校验),关闭时保持三键不得携带;④校验先于用户查询与锁定检查(防爆破第一道闸),不写审计、不计入登录失败锁定;验证码一次性消费(无论登录成败即作废),缺失/错误/过期/未知captchaId统一返回新增错误码400 INVALID_CAPTCHA(「验证码错误或已过期」,不区分具体原因);⑤验证码内存存储(单实例部署,签发上限1000条防刷量,过期惰性清理);⑥前端登录页加载时探测GET /auth/captcha,enabled=true展示验证码输入与图片(点击图片刷新),登录失败自动刷新;无数据库结构变更。OpenAPI同步升级为1.32.0。
版本1.33,超级管理员隐藏与机构树管理范围裁剪(安全需求7/8):①新增环境变量DMS_ALLOW_ADMIN_LOGIN(双版同名,接受1/true/yes/on,缺省true允许)——设为false后三处行为同时生效:内置admin登录一律按用户名不存在处理(查库前短路,报错、审计、锁定行为与密码错误完全一致,不泄露账号存在);GET /api/v1/users用户列表隐藏内置admin账号(分页total同步扣减);POST /api/v1/users创建用户名admin返回400 INVALID_ARGUMENT(「不能创建该名称的用户」,不再报「用户名已存在」,先于唯一性查重)。②机构树管理范围裁剪——GET /api/v1/organizations/tree按请求者生效管理范围(1.30④解析规则)裁剪:锚点管理员仅返回管理范围子树内机构(森林形式,根从锚点机构起,范围外机构与其祖先链不出现,keyword/status过滤在裁剪后的集合内进行),全局管理员(内置admin与锚点解析为NULL/全局)不受限返回全量;机构管理页、用户管理页与用户编辑「所属机构」下拉均消费本接口,裁剪后各处仅见有管理权限的机构树。③修复双端不一致:DMS_CAPTCHA_ENABLED=false时Python版登录仍被验证码校验拦截(INVALID_CAPTCHA)无法登录的问题,开关关闭时直接放行校验(对齐Java版,登录回归三键契约)。无数据库结构变更。OpenAPI同步升级为1.33.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。