Repository navigation
feat(tasks): group extension folder downloads - #1594
wufeng5702 wants to merge 10 commits into
Conversation
|
@monkeyWie This is the backend half of the task group plan from #1578, opened as a draft to settle the direction before the UI work. The three points from the issue use the conservative default (no |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1594 +/- ##
==========================================
+ Coverage 73.06% 73.47% +0.40%
==========================================
Files 106 106
Lines 16565 16827 +262
==========================================
+ Hits 12104 12364 +260
- Misses 3159 3163 +4
+ Partials 1302 1300 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@wufeng5702 任务组这块需要先等 #1595 合并,之后再基于更新后的 main 继续。 |
d56a860 to
650488d
Compare
There was a problem hiding this comment.
目前的子任务调度、组操作展开、组卡片和文件浏览可以继续复用。我们这边把方案进一步收敛了一下,需要按下面的方向调整,所以先 request changes。
本次意见基于 650488d934845d09676d47442caae646c0aaf4b6。最新提交已补上空组名校验,并将白名单内无法预留新目录的情况改为报错,这两项不再列为待修问题。
1. 保留现有 batch API,通过 group: true 开启分组创建。
不新增任务组创建接口。将目前的 group: {id, name, path} 改为布尔开关,另提供 groupName;opts.path 表示下载目标父目录。group 未传或为 false 时,保持原有批量创建行为和返回值。
group 为 true 时,后端校验名称、生成组 ID、预留顶层目录,并给本批成员设置相同的 parentId。组 ID 复用现有任务 ID 的生成算法,不由前端传入,也不再覆盖后端生成的 ID。创建接口不再通过客户端指定 ID 隐式追加到已有组。
组模式需要返回 groupId、最终名称、目录及 taskIds;对应的 Flutter API、响应模型和窗口 RPC 返回类型/编解码也需要同步适配,不能继续假设所有 batch 响应都是 List。
2. 去掉 Task.type 和无 fetcher 的父组 Task,按 parentId 聚合。
任务列表继续平铺返回实际下载任务。parentId 为空时独立展示,相同的非空 parentId 聚合为一个组。前端不再依赖 isGroup、父组记录存在与否,也不要求 API 返回 children 或后端汇总组状态。
组 ID、名称及最终目录仍需在后端持久化,作为分组元数据维护,并通过现有查询附带给前端,不新增查询路由。不能只保存 parentId,再由前端从文件路径猜测组名和根目录。这里请把查询字段一起落实,保证重启后能完整恢复分组展示。
暂停、恢复、删除沿用现有接口:组 ID 在后端按 parentId 展开到成员,混合传入组 ID 和成员 ID 时去重,并处理最后一个成员删除后的元数据清理。
3. 前端只对命名的多文件扩展结果自动成组。
成组条件为:扩展解析结果、res.name.trim().isNotEmpty,并且 res.files.length > 1,三个条件同时成立。数量按扩展原始解析结果判断。
目前 _submitResolved 使用 name 非空“或”文件数大于 1,需要改为“且”。移除域名/Download 等组名兜底。不满足条件时,按原有单任务或普通 batch 创建;普通多文件 BT 仍然是一个下载任务。不增加合并下载开关。
4. 多行串行 resolve 必须配套改造解析预览组件。
目前 _confirm 仍通过 urls.length > 1 直接绕过 resolve;ResolveFileTree 也仍然只接收一份 files 和一组 List 文件索引,这部分尚未支持多来源解析结果。PR 中已修改的任务详情文件树,不能替代创建前的解析预览改造。
需要将多行链接依次 resolve,统一展示待创建项。每个根节点代表一个独立任务或扩展组,各自保留原始请求、rid、文件列表、选中项和创建参数。多文件 BT 的根仍代表一个下载任务;扩展组下则是多个下载成员;普通目录节点不代表任务组。
节点需要稳定的本地标识,用于区分同名来源,这个标识不作为 groupId 提交。文件选择应使用“待创建项标识 + 原始文件索引”定位,避免多个结果的索引 0 或同名路径串到一起。
提交时按根节点生成请求:BT 保留自己的 rid/SelectFiles;满足成组条件的扩展结果调用 batch(group: true);其他资源走原有创建方式。完全未选择的根不提交。分别记录解析和创建结果,重试时不要重复提交已创建成功的项。
5. 统一组目录规则,保留现有全局队列。
组创建必须拥有真实顶层目录,不再保留 path 为空的“纯标签组”创建模式。后端根据目标父目录和 groupName 确定最终目录,处理重名,再将成员路径放到该目录内并保留相对结构。请保留本次已修复的名称校验和白名单冲突报错行为。
所有实际下载任务继续共用原来的全局队列,组不占名额;有任意成员下载中,前端组卡片就显示下载中。这部分方向已经符合预期,不需要改成组级调度。
TODO
- batch 请求改为 group 布尔开关 + groupName,普通模式保持兼容。
- 后端统一生成组 ID,在成员首次持久化及调度前设置 parentId。
- 移除 Task.type、父组 Task 及对它们的依赖,落实分组元数据的持久化、查询和清理。
- 调整组模式创建响应,以及 Flutter API、模型和窗口 RPC 编解码。
- 前端按 parentId 直接聚合,复用现有卡片、搜索、统计、状态汇总及文件浏览能力。
- 自动成组条件改为扩展 name 非空且 files.length > 1,移除前端组 ID 和默认组名生成。
- 多行链接串行 resolve,并建立多来源的待创建项模型。
- 解析文件树支持多根、来源隔离、稳定节点标识及按来源保存文件选择。
- 适配全局/根/目录/文件选择、半选、类型筛选、数量和大小统计。
- 按待创建项生成提交计划,正确保留 rid/req、文件选择和路径;处理逐项解析/创建失败及重试。
- 强制统一组目录,保留重名预留、路径边界、空名称校验和白名单冲突报错。
- 明确 batch 部分失败的返回结果和清理规则,避免客户端因丢失成功结果而重复创建。
- 补测试:普通 batch 兼容、扩展成组条件矩阵、多来源/同名根/相同文件索引/部分取消,以及重启后的分组信息恢复。
- 保留组操作、混合 ID 去重和并发回归:上限 5 时组内 6 个任务为 5 下载、1 等待;已有 2 个独立任务下载时,组内为 3 下载、3 等待。
f00369d to
16e4888
Compare
16e4888 to
146f7d4
Compare
There was a problem hiding this comment.
当前方案的主体改造已经落实:group 布尔开关、后端生成组 ID、独立组元数据、parentId 聚合、串行 resolve 和多来源文件树都可以继续保留。
下面是基于 146f7d480459ae4d9e155006d8a1742c1b0ba58e 的本轮补充要求和 TODO。这版覆盖之前关于“普通 batch 继续返回数组”“组响应返回 groupId/name/path/items”“按失败成员补建”的要求,请以本文为准。
1. 统一 batch API,请求兼容,响应明确做 breaking change。
继续使用 POST /api/v1/tasks/batch,不新增任务组创建接口。保留 group、groupName、opts、reqs;给每个 reqs 成员增加可选 rid,原来的 req + opts 请求保持有效。
成员沿用单任务创建的分派规则:rid 非空时复用已解析资源,否则使用 req,两者都没有则该项失败。保留对应 opts,尤其是 BT 的 SelectFiles。rid 路径需要在首次持久化和调度前支持 parentId,不能创建后再补成员关系。
普通批次和组批次的 data 始终返回对象,统一为:
{"taskIds":["task-a","task-b"]}组创建成功时,仅增加 parentId:
{"parentId":"backend-generated-id","taskIds":["task-a","task-b"]}parentId 与成员 Task.parentId 一致。创建响应不再返回 groupId、name、path、items;外层 code/msg 保持原样。组名和重名后的真实目录继续持久化,并通过现有任务列表/详情中的组元信息提供。这里破坏兼容的是普通 batch 从数组改为对象返回,请求只是增加可选字段。
2. 统一批量创建的成功判定和提交方式。
普通批次和组批次都遍历全部成员:至少一个任务记录创建成功,整个请求就成功,返回成功的 taskIds;只有全部创建失败才返回 API 错误。组模式全部失败时清理空组元数据和本次预留目录。部分失败时保留已经创建成功的任务,不整体回滚。
任务记录创建成功后发生的下载错误,属于原有任务生命周期,不应使前端重新创建该任务。
前端按选中的解析结果先生成提交计划:每个扩展任务组单独调用一次 batch(group: true),其余独立资源汇总调用一次普通 batch。比如 A 是扩展组、B 是无组名的扩展多资源、C 是 BT、D 是 HTTP,最终应是 A 一次组请求,B/C/D 一次普通批量请求。BT/HTTP 可使用自己的 rid,扩展拆出的资源使用 req,并保留各自选项和目录。
收到成功响应就结束该批次的创建,不再把个别成员失败作为该批次 createError,也不再通过 createdFiles/items 补建失败成员。不需要为此增加向原组续建或客户端生成组 ID 的机制。同一面板内可以跟踪已完成的提交批次,以避免重复点击重复提交;不再逐文件补建。
3. 立即打开解析面板,串行实时更新,根节点只有三种解析状态。
点击确认后立即打开面板,先为每个 URL 创建稳定的来源根节点,再串行 resolve;不能等全部 resolve 完才弹出。
- loading:包括等待解析和正在解析,复选框 disabled。
- 解析成功:显示正常资源名称、文件树和大小,允许勾选。
- 解析失败:对应根节点保留失败状态,复选框 disabled,不参与创建;单项失败不阻断后续解析。
只要存在 loading(包括队列中尚未解析的 URL),创建按钮就禁用。全部解析结束且至少有一个选中资源时才允许创建。失败根节点不需要用户手动取消,不能因其存在而阻止其他成功资源创建。若提供单项解析重试,重试期间也遵循相同的 loading 和按钮规则。
保留本次已有的来源隔离、本地节点 ID、来源 ID + 原始文件索引选择模型及全局/根/目录/文件勾选。新结果到来时不能重置用户已完成项的选择。取消面板后停止后续解析,避免晚返回结果更新已关闭界面。
4. 解析面板采用已确认的行内图标设计,名称和来源信息收敛到文件树节点。
弹窗标题统一为“文件列表”,无论一个来源还是多个来源,都不再用某一份 res.name 替换弹窗标题。当前标题中的 sources.length == 1 ? res.name : ... 分支应移除;res.name 显示在对应来源的文件树根节点。HTTP 成功后显示文件名,BT 显示资源名称;没有解析出名称时使用来源 URL。与某个来源有关的信息放在其根节点或 tooltip,不放到公共标题区。
具体视觉规则:
- 根节点图标按资源区分:BT 使用现有的 BT 文件夹图标(
GopeedIcons.folderBt),任务组使用普通文件图标(GopeedIcons.file)。展开/收起箭头独立显示,不能因为节点可展开就统一使用普通文件夹图标。 - 顶部只保留标题和关闭操作,移除已解析数量、成功/失败数量、当前解析序号等汇总状态。
- loading 只显示行内旋转图标,hover/focus/touch 可查看“正在解析”或“等待解析”的说明。
- 解析失败只显示行内错误图标,hover/focus/touch 查看失败原因;如果支持重试,可由该图标提供入口,不额外铺开一行错误文案或“图标 + 解析失败”标签。
- 解析成功不显示绿色成功图标或“解析成功”标签,恢复普通解析完成的文件树样式,显示正常图标、名称和大小。
- 文件树保留文件名称/大小列、展开收起、复选框和底部类型筛选/已选数量与大小;创建按钮按解析状态控制。
- 图标需要可访问名称,移动端能通过触摸查看说明,不能只依赖桌面 hover。
5. 简化解析复用和重试状态。
保留当前面板内各来源的解析结果和选择状态,但移除 _pendingDownloads 按 URL 跨次确认复用旧解析结果的机制。取消预览返回表单后再次确认,尚未提交的来源重新 resolve,避免 URL 相同但 Cookie、headers、method/body、代理等参数已变时继续使用旧 rid。
这里不涉及移除后端正常的 rid 解析缓存;需要去掉的是前端按 URL 跨轮复用,以及逐个失败成员补建带来的额外状态。
TODO:API / 后端
- batch 成员增加可选 rid,复用已解析任务创建和 BT 文件选择校验,保留 req 路径。
- 所有 batch 统一返回
{taskIds, parentId?},移除数组特殊序列化及 groupId/name/path/items 响应字段。 - 普通与组模式统一遍历全部成员;至少一项创建成功即成功,全部失败返回错误并清理空组。
- 保留已完成的后端组 ID 生成、独立元数据、首次持久化前设置 parentId、目录预留和重名/边界校验。
- 同步 REST、内置 API Service、Flutter 模型和窗口 RPC 返回类型/编解码;更新文档并明确响应的 breaking change。
TODO:前端创建流程
- 先生成提交计划:每组一个 group batch,所有非组资源一个普通 batch;正确携带 rid/req、opts、SelectFiles 和相对目录。
- 成功响应按批次完成处理,移除 items/createdFiles 驱动的逐文件补建;防止重复提交已成功的批次。
- 保留扩展 name 非空且原始 files.length > 1 的自动成组条件,不增加合并下载交互,BT 仍是一个独立任务。
- 移除按 URL 跨次确认复用解析结果的逻辑,当前预览状态与已完成提交状态分开管理。
TODO:解析面板
- 点击后立即打开面板,每个 URL 一个根节点,串行解析并实时更新。
- 根节点维护 loading / 成功 / 失败三态;失败和 loading 禁选,有任一 loading 时禁用创建。
- 不再要求用户手动排除失败来源,失败项不阻断成功资源提交。
- 弹窗标题固定“解析资源”,去掉单来源 res.name 动态标题;资源名与来源信息放在树节点。
- 移除顶部汇总状态,loading/失败仅用图标及 tooltip,成功项不显示额外状态标记。
- 根节点图标:BT 使用
GopeedIcons.folderBt,任务组使用GopeedIcons.file,展开箭头与资源图标分开。 - 保留来源隔离、同名根区分、原始文件索引和选择状态;适配窄屏及图标的键盘/触摸说明。
- 取消后停止后续解析并安全处理晚返回结果。
TODO:验证
- 请求兼容、统一响应、req/rid 混合批量、BT 文件选择及 rid 消费规则。
- 普通和组批次的全部成功、部分成功、全部失败;部分成功不得再次补建。
- A 组 + B 扩展独立资源 + C BT + D HTTP 的提交计划应只有两个请求。
- 面板立即显示、实时状态、失败禁选、loading 禁止创建、成功项正常展示和固定标题。
- 同名根/相同文件索引/多来源逐项更新不丢选择;重新确认表单不使用旧请求的解析结果。
- 保留现有元数据恢复、组操作和全局队列回归测试:最大并发 5 时组内 6 个成员为 5 下载 + 1 等待,已有 2 个独立任务时组内为 3 下载 + 3 等待。
d0068a4 to
8b0ecc2
Compare
There was a problem hiding this comment.
基于最新提交 8b0ecc2c8490a341eacde266ceeaf5a92d5d5c53 实际验收后,还有两处前端交互需要调整。本条为新增的 Request changes,补充之前的评审要求。
1. 统一移除文件树中多余的解析来源父节点,并调整任务组图标。
当前文件树会为每个解析来源保留一层父节点,导致普通 HTTP 单文件也出现“父节点 → 文件”的重复层级。这需要统一调整,不限于扩展返回的 res.name 为空的情况。
预期行为:
- 等待解析、正在解析时,以 URL 行作为临时占位;解析失败时保留该行,沿用现有错误图标、tooltip 和禁选规则。
- 解析成功后,用实际资源替换占位节点:普通单文件直接作为根节点,无任务组的多个文件直接作为多个根级资源,并保留文件自身的相对目录结构。
- 只有真实的任务组或资源目录才保留父节点。例如扩展任务组保留组根节点,BT 保留自身的目录结构,不再额外套一层来源节点。
- 文件树中的任务组父节点使用普通文件夹图标,展开/收起箭头独立显示;BT 继续使用专用的 BT 文件夹图标(
GopeedIcons.folderBt)。这条覆盖此前“任务组使用普通文件图标(GopeedIcons.file)”的要求。 - 去掉可见的包装层后,内部仍需保留 sourceId、
rid/req、原始文件索引、选择状态和创建参数,保证多来源同名文件或相同索引不会串选,创建请求仍能正确归属到各自来源。自动成组条件和提交计划保持现有约定。
核心原则:文件树展示实际资源结构,解析来源关系由内部模型维护,不额外增加展示层级。
2. 任务列表中的任务组,点击应展开/收起子任务,子任务复用普通任务交互。
当前 HomePage._handleTaskPressed 遇到有目录的任务组会调用 _openTask,桌面端因此直接打开系统目录。这不符合任务组作为任务列表上层封装的交互预期。
预期行为:
- 在正常浏览模式下,点击任务组行,在当前任务列表内展开/收起其子任务,不打开组详情、系统目录或文件浏览弹窗。
- 展开的内容是实际的子任务列表,不是把成员文件平铺成文件树。组内一个多文件 BT 仍然是一个子任务。
- 每个子任务复用普通任务的展示及事件处理:单击打开该子任务自己的详情,右键显示该子任务自己的菜单,暂停、恢复、删除、打开、定位等操作按普通任务的状态和能力执行,操作目标使用该子任务 ID。
- 组本身已有的组级暂停、恢复、删除等操作继续保留;批量选择模式下继续遵循原有选择逻辑。桌面和移动布局都需要支持组的展开/收起。
TODO
- 统一移除解析成功后的多余来源包装层,覆盖普通 HTTP 单文件及无任务组的多文件,保留真实目录、来源隔离和选择映射。
- 文件树任务组父节点改用普通文件夹图标;BT 保留专用 BT 文件夹图标。
- 任务组行增加展开/收起子任务列表的交互,替换当前点击打开目录的分支。
- 子任务接入普通任务的详情入口和上下文菜单,确保所有操作使用成员 ID。
- 补充回归验证:loading URL 行到成功文件根节点的替换(包含普通 HTTP 单文件)、多来源同名文件选择隔离、任务组/BT 父节点图标、任务组展开/收起、成员单击详情和右键操作,以及组内多文件 BT 仍作为一个子任务展示。
00175b5 to
3c89f07
Compare
Closes #1578.
Extension folder downloads currently create one task card per file. This PR groups them into a single card with combined progress, size and status, while scheduling each member independently in the global queue.
parentId. Support opening the group folder on desktop and browsing its files on mobile and Web.The existing batch endpoint accepts an optional
ridper item. Both ordinary and grouped batches now return{taskIds, parentId?}, replacing the ordinary batch array response. The backend generates group IDs and directories; creation does not append to an existing group.Testing