docs: 记录文档库多选批量分享设计(#25)。
明确整份覆盖、交集预选、batch-shares 事务与 Web 弹窗扩展范围,供实现对照。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
391e5f66f5
commit
bf89a1f301
1 file changed
+115
@@ -0,0 +1,115 @@
|
||||
# 文档库多选批量设置分享(工单 #25)
|
||||
|
||||
工单:https://gitea.aichengyan.com/cykj/shanxia2.0/issues/25
|
||||
|
||||
## 背景与问题
|
||||
|
||||
Web 文档库管理(`/#/doclib/enter`)工具栏「设置分享」当前仅在恰好选中 1 份且可管理的文档时可用(`actionsFor.share`),弹窗与接口均为单文档。用户需要多选文档后统一设置分享人员。
|
||||
|
||||
## 已确认决策
|
||||
|
||||
| 项 | 结论 |
|
||||
| --- | --- |
|
||||
| 保存语义 | 整份覆盖:弹窗所选 `userIds` 写入每一份所选文档,替换原分享人;空列表表示全部收回 |
|
||||
| 弹窗初始勾选 | 所选文档现有分享人的交集;无共同人员则为空 |
|
||||
| 修改端 | Web 管理端 + 后端批量 API |
|
||||
| 失败策略 | 整批事务:全部成功或全部回滚 |
|
||||
| 实现方案 | 新增 `batch-shares` + 扩展现有 `ShareDialog`(方案 A) |
|
||||
|
||||
## 范围
|
||||
|
||||
**包含**
|
||||
|
||||
- `web-admin`:`doclib/enter` 列表工具栏、`ShareDialog`、`actionsFor`、API 封装与单测
|
||||
- `backend`:批量分享路由、schema、服务层事务与单文件写入逻辑复用
|
||||
|
||||
**不包含**
|
||||
|
||||
- 小程序 / 移动 H5
|
||||
- 「只追加不收回」、部分成功返回清单
|
||||
- 文件夹级分享
|
||||
|
||||
## 后端设计
|
||||
|
||||
### 接口
|
||||
|
||||
`POST /web/doclib/files/batch-shares`
|
||||
|
||||
请求体(`extra=forbid`):
|
||||
|
||||
```json
|
||||
{
|
||||
"ids": ["file-id-1", "file-id-2"],
|
||||
"userIds": ["user-id-a", "user-id-b"]
|
||||
}
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
- `ids`:`min_length=1`,`max_length=100`(对齐 `DeleteFilesBody`)
|
||||
- `userIds`:`max_length=500`(对齐 `ShareBody`)
|
||||
- 去重后的 `ids` 均须当前用户可管理;任一份不可管理则 4xx,整批不写
|
||||
- 人员须同租户且启用;自动排除各文档上传人(与现 `set_shares` 一致:`requested - {create_by}`)
|
||||
- 非法 / 停用 / 跨租户人员 → 400,整批回滚
|
||||
|
||||
响应:成功后返回更新后的文档列表(字段与现有单文档分享成功返回一致的序列化)。
|
||||
|
||||
### 事务与复用
|
||||
|
||||
- 现有 `DoclibService.set_shares` 在单文件路径末尾 `commit`。批量路径须在**全部文件写入完成前不 commit**。
|
||||
- 抽取内部方法(名称示例:`_apply_shares(file_id, user_ids)`)完成单文件差量更新(含 lock);`set_shares` = `_apply_shares` + `commit` + 返回序列化;`set_shares_batch` = 校验全部可管理 → 校验人员 → 对每个 id 调用 `_apply_shares` → 一次 `commit` → 返回列表。
|
||||
- 任一步抛错依赖会话回滚,不留下部分文档已改的状态。
|
||||
|
||||
路由注册位置:与现有 `PUT /web/doclib/files/{file_id}/shares`、`POST .../batch-delete` 同模块(`backend/app/api/doclib.py`)。
|
||||
|
||||
## 前端设计
|
||||
|
||||
### 工具栏与权限
|
||||
|
||||
- `actionsFor.share`:`rows.length > 0 && rows.every((row) => row.canManage)`(与删除多选门槛一致,取消「仅 1 条」限制)。
|
||||
- 工具栏「设置分享」对 `selected` 全量打开弹窗,不再只取 `selected[0]`。
|
||||
- 详情抽屉单文档「设置分享」仍传入单元素数组或单文档,行为不变。
|
||||
|
||||
### ShareDialog
|
||||
|
||||
- Props 改为支持多文档(例如 `documents: DoclibDocument[]`);打开时:
|
||||
- 摘要列出所选文档名称(数量较多时可截断并注明总数)
|
||||
- `userIds` 初始 = 分享人 id **交集**
|
||||
- 候选人员:交集中的人员 ∪ 远程搜索结果;排除各文档上传人(多文档时排除所有相关 `ownerId`)
|
||||
- 保存:统一调用批量接口 `saveDoclibBatchShares(ids, userIds)`(单文档也走批量,少分支);成功提示后关闭并刷新列表 / 更新详情。
|
||||
|
||||
### 纯函数(便于单测)
|
||||
|
||||
- `shareUserIntersection(documents): string[]` — 交集;空文档列表返回 `[]`
|
||||
- 可选:`canShareSelection(rows)` 与 `actionsFor` 共用语义
|
||||
|
||||
### API
|
||||
|
||||
在 `web-admin/src/api/doclib.ts` 增加:
|
||||
|
||||
`saveDoclibBatchShares(ids: string[], userIds: string[])` → `POST .../files/batch-shares`
|
||||
|
||||
单文档 `saveDoclibShares` 可保留供兼容/测试,页面路径以批量为准。
|
||||
|
||||
## UI 规范
|
||||
|
||||
- 沿用现有 `doclib-dialog dialog-form-layout`、Fresh Mint、P12 类弹窗;不新造布局与主色。
|
||||
- 按钮图标规则与现页一致(Close / Check 等已有用法)。
|
||||
|
||||
## 验收
|
||||
|
||||
1. 多选可管理文档后可打开「设置分享」;含不可管理项时按钮禁用。
|
||||
2. 打开时勾选为交集;无共同分享人时为空。
|
||||
3. 保存后所选文档分享人员均等于弹窗所选集合(含清空)。
|
||||
4. 故意混入无权限文档(若后端被直接调用)→ 整批不改。
|
||||
5. 详情页单文档分享仍可用。
|
||||
6. 单测覆盖交集函数与 `actionsFor.share`;后端/服务层覆盖批量成功与权限失败回滚(按项目既有测试方式)。
|
||||
|
||||
## 风险
|
||||
|
||||
- `set_shares` 重构时须保证单文档路径回归(commit 时机、上传人排除、软删恢复历史 share 行)。
|
||||
- 批量上限 100 与删除一致;超大选区依赖现有表格多选,不另做分片 UI。
|
||||
|
||||
## 状态
|
||||
|
||||
设计已获用户确认(会话中「按照推荐」方案 A,「OK,请继续」批准落盘)。待用户审阅本文件后进入实现计划。
|
||||
Reference in new issue
Block a user