refactor: 全面重构代码并加白话注释,移动全局并行,去掉去向/重复记录

- 重构后端与前端,全程大白话注释(不懂程序也能看懂)
- 移动阶段改为全局并行(MoveAll,工人数=CPU核心数)
- 去掉记录每个文件搬去哪了(删除去向回填,解决写库卡顿)
- 去掉重复文件标记与统计:重复判定改为 MD5+路径都相同才算,重复则原地忽略不移动;MD5 相同但路径不同仍照常搬
- 媒体库去掉归类目录列,看板去掉重复/归类分布统计
This commit is contained in:
2025-05-11 21:05:35 +08:00
parent f915a9b1ac
commit 8979c744a8
26 changed files with 1044 additions and 683 deletions
+19 -9
View File
@@ -1,24 +1,31 @@
// 状态订阅发布:将 ScanService 的状态变更实时推送给订阅者(WebSocket 等)。
// 状态订阅/发布中心:把 ScanService 的状态变更实时"广播"给所有订阅者。
//
// 大白话理解:这是个广播站。后台干活时进度一变,就通过广播站喊一嗓子;
// 谁订阅了(比如 WebSocket 连接),谁就收到。没有人听的时候也不影响干活。
//
// 还有个贴心小设计:每个订阅者面前的信箱最多存一条消息。
// 如果进度变得太快、接收方来不及处理,就只留最新一条——反正看进度的人
// 关心的是"现在到哪了",而不是"中间每一刻到哪了"。
package service
import "sync"
// StatusListener 状态订阅通道(缓冲 1,保底容纳一条未消费状态)
// StatusListener 订阅者的"信箱"(一个缓冲 1 的管道)。
type StatusListener chan ScanStatus
// StatusHub 管理状态订阅者;发布时若消费者来不及消费则丢弃旧消息,
// 保证订阅者拿到的始终是最新状态。
// StatusHub 广播站:管理所有订阅者,负责把状态推给每个人。
type StatusHub struct {
mu sync.RWMutex
subs map[StatusListener]struct{}
mu sync.RWMutex // 保护订阅者名单
subs map[StatusListener]struct{} // 当前所有订阅者
}
// NewStatusHub 创建状态订阅中心
// NewStatusHub 建一个广播站。
func NewStatusHub() *StatusHub {
return &StatusHub{subs: make(map[StatusListener]struct{})}
}
// Subscribe 注册订阅,返回频道与取消函数。取消后频道被关闭,写方需停止发送。
// Subscribe 让调用方订阅进度。返回它的信箱,和一个"退订"函数。
// 退订后信箱会被关闭(写方要自己注意别再往里面塞)。
func (h *StatusHub) Subscribe() (StatusListener, func()) {
h.mu.Lock()
defer h.mu.Unlock()
@@ -34,7 +41,9 @@ func (h *StatusHub) Subscribe() (StatusListener, func()) {
}
}
// Publish 向所有订阅者推送最新状态(非阻塞;缓冲满时覆盖为最新状态)
// Publish 把最新状态推给所有订阅者。
// 谁的信箱满了(来不及消费),就把里面的旧消息挤掉、换上最新的一条。
// 整个过程不阻塞,广播站永远不会因为某个订阅者太慢而卡住。
func (h *StatusHub) Publish(st ScanStatus) {
h.mu.RLock()
defer h.mu.RUnlock()
@@ -42,6 +51,7 @@ func (h *StatusHub) Publish(st ScanStatus) {
select {
case ch <- st:
default:
// 信箱满了:清掉旧消息,放最新的进去
select {
case <-ch:
default: