feat: 扫描整理并行化、WebSocket 实时进度与统计看板
- 移动阶段并行处理文件,按原子计数上报累计进度 - 扫描遍历阶段实时上报已发现文件数,界面不再卡在 0/0 - 新增 /api/scan/ws WebSocket 实时推送扫描状态;前端断线指数退避自动重连,并以低频轮询兜底 - 新增统计看板与媒体库筛选/排序,目录选择器路径不存在时回退到最近可用父目录 - 存放目录支持自定义并持久化,默认输出到 KMP已整理
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
// 状态订阅发布:将 ScanService 的状态变更实时推送给订阅者(WebSocket 等)。
|
||||
package service
|
||||
|
||||
import "sync"
|
||||
|
||||
// StatusListener 状态订阅通道(缓冲 1,保底容纳一条未消费状态)
|
||||
type StatusListener chan ScanStatus
|
||||
|
||||
// StatusHub 管理状态订阅者;发布时若消费者来不及消费则丢弃旧消息,
|
||||
// 保证订阅者拿到的始终是最新状态。
|
||||
type StatusHub struct {
|
||||
mu sync.RWMutex
|
||||
subs map[StatusListener]struct{}
|
||||
}
|
||||
|
||||
// NewStatusHub 创建状态订阅中心
|
||||
func NewStatusHub() *StatusHub {
|
||||
return &StatusHub{subs: make(map[StatusListener]struct{})}
|
||||
}
|
||||
|
||||
// Subscribe 注册订阅,返回频道与取消函数。取消后频道被关闭,写方需停止发送。
|
||||
func (h *StatusHub) Subscribe() (StatusListener, func()) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
ch := make(StatusListener, 1)
|
||||
h.subs[ch] = struct{}{}
|
||||
return ch, func() {
|
||||
h.mu.Lock()
|
||||
if _, ok := h.subs[ch]; ok {
|
||||
delete(h.subs, ch)
|
||||
close(ch)
|
||||
}
|
||||
h.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Publish 向所有订阅者推送最新状态(非阻塞;缓冲满时覆盖为最新状态)
|
||||
func (h *StatusHub) Publish(st ScanStatus) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
for ch := range h.subs {
|
||||
select {
|
||||
case ch <- st:
|
||||
default:
|
||||
select {
|
||||
case <-ch:
|
||||
default:
|
||||
}
|
||||
ch <- st
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user