状态:已完成(2026-08-22)
测试结果:
在"编辑"功能里为方案增加关联关系:
已确认的三点决策:
补充约定(未显式询问,按"方案"语义推定):
update_document 的类型约束一致;ui-dictionaries 是强枚举绑定体系:字典 key 必须对应后端 StrEnum(DICTIONARY_ENUMS),JSON 集合与枚举成员必须完全一致,否则启动报错。而关系类型是开放集合(预置 + 用户自定义文本),不适合枚举绑定。
新建 backend/dms/resources/document-relation-presets.zh-CN.json:
{
"presets": [
{ "code": "BASIS", "label": "依据", "sortNo": 10 },
{ "code": "SUPPORT", "label": "支撑", "sortNo": 20 },
{ "code": "REFERENCE", "label": "参照", "sortNo": 30 }
]
}
doc_document_relation.relation_type 直接存最终文本(预置 label 或用户自定义文字):
沿用挂载关系表的成熟模式(软删 + 生成列唯一约束):
| 列 | 类型 | 说明 |
|---|---|---|
| id / created_by / updated_by / created_at / updated_at / deleted_at / is_deleted / row_version | BusinessTableMixin | 通用字段 |
| document_id | BigInteger FK doc_document.id RESTRICT | 发起方 |
| related_document_id | BigInteger FK doc_document.id RESTRICT | 被关联方(须为 MAIN) |
| relation_type | String(64) NOT NULL | 关系文本 |
| sort_no | Integer NOT NULL default 0 | 行序(10 步长) |
| active_marker | Computed IF(is_deleted=0,1,NULL) | 软删唯一约束载体 |
约束与索引:
uq_doc_document_relation_active (document_id, related_document_id, active_marker)——同一发起方对同一目标至多一条有效关联;ck document_relation_no_self: document_id <> related_document_id;ix_doc_document_relation_document_active (document_id, is_deleted, sort_no);ix_doc_document_relation_related_active (related_document_id, is_deleted)。| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| GET | /documents/relation-presets | 登录 | 预置关系清单(启动期加载校验) |
| GET | /documents/{id}/relations | 登录 | 查看关联(走与 get_document 相同的访问控制) |
| PUT | /documents/{id}/relations | 管理员 | 整体替换保存 |
PUT 请求体:{ "relations": [{ "relatedDocumentId": "…", "relationType": "…" }], "rowVersion": n };空数组 = 清空全部关联。
服务层要点(仿 bind_attachments / update_document):
with transaction() + 发起方 with_for_update() 行锁;类型限 MAIN/SUB_PLAN;require_document_password(document, EDIT, …)(设密主案编辑关联同样需密码);row_version += 1(与挂载/解挂一致);EDIT_DOCUMENT 动作(detail 标注 scope=relations 与前后清单),不扩展 sys_audit_log 的 CHECK 约束(避免迁移面扩大);is_deleted=False(目标进回收站后隐藏,恢复后自动重现)。编辑弹窗(EditMetadataModal):
[关系下拉(预置 + "自定义…")] [自定义文本输入(选中自定义时显示)] [方案下拉(复用 useReferenceData 的 mainPlans,排除自身)] [删除];matchPresetCode(text, presets) 派生是否自定义;查看窗口(FilePreviewModal,modal 模式):
关系 · 方案名,点击即跳转;navigated: DocumentSummary | null 状态,current = navigated ?? item;点击关联项 → documentApi.detail(targetId) → setNavigated(detail),预览/正文/说明/关联全部以 current 驱动重新加载;类型与 API client:
frontend/src/features/documents/documentRelationTypes.ts:DocumentRelationItem / DocumentRelationListResult / DocumentRelationPreset(s) / UpdateDocumentRelationsRequest + 运行时守卫 + matchPresetCode 纯函数;listRelations / relationPresets;documentMutationApi 增 updateRelations(带密码头)。export PYTEST_DISABLE_PLUGIN_AUTOLOAD=1。