This commit is contained in:
truewhile
2026-08-26 12:11:56 +08:00
parent 98ca766a37
commit 4764c09572
2 changed files with 83 additions and 29 deletions
+16
View File
@@ -113,6 +113,7 @@ func NewStrmService(cfg *config.Config, log *zap.Logger, repos *repository.Conta
// Start 启动下载/上传队列 worker、定时同步巡检、115 token 刷新与队列清理。 // Start 启动下载/上传队列 worker、定时同步巡检、115 token 刷新与队列清理。
func (s *StrmService) Start(ctx context.Context) { func (s *StrmService) Start(ctx context.Context) {
s.sync115RelayKey(ctx) s.sync115RelayKey(ctx)
s.recoverInterruptedSyncs(ctx)
downloadThreads := s.strmIntSetting(ctx, StrmSettingDownloadThreads, 3) downloadThreads := s.strmIntSetting(ctx, StrmSettingDownloadThreads, 3)
if downloadThreads < 1 { if downloadThreads < 1 {
downloadThreads = 1 downloadThreads = 1
@@ -141,6 +142,21 @@ func (s *StrmService) Start(ctx context.Context) {
zap.Int("upload_threads", uploadThreads)) zap.Int("upload_threads", uploadThreads))
} }
// recoverInterruptedSyncs 在服务启动时自愈重置因服务重启遗留的 running 状态。
func (s *StrmService) recoverInterruptedSyncs(ctx context.Context) {
paths, err := s.repo.StrmSyncPath.List(ctx)
if err == nil {
for i := range paths {
p := &paths[i]
if p.LastSyncStatus == model.StrmSyncRecordRunning {
p.LastSyncStatus = model.StrmSyncRecordCanceled
p.LastSyncMessage = "服务重启,已重置同步状态"
_ = s.repo.StrmSyncPath.Update(ctx, p)
}
}
}
}
func (s *StrmService) Stop() { func (s *StrmService) Stop() {
s.stopOnce.Do(func() { close(s.stopCh) }) s.stopOnce.Do(func() { close(s.stopCh) })
} }
+67 -29
View File
@@ -104,15 +104,27 @@ func (s *StrmService) StartSync(ctx context.Context, pathID string, syncType ...
return nil return nil
} }
// CancelSync 取消正在进行的同步。 // CancelSync 取消正在进行的同步(若为僵尸运行状态则直接自愈重置)。
func (s *StrmService) CancelSync(ctx context.Context, pathID string) error { func (s *StrmService) CancelSync(ctx context.Context, pathID string) error {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock()
cancel, exists := s.running[pathID] cancel, exists := s.running[pathID]
if !exists { if exists {
return errors.New("该目录当前没有进行中的同步") delete(s.running, pathID)
}
s.mu.Unlock()
if exists && cancel != nil {
cancel()
}
// 无论内存中是否活跃,确保同步目录状态正确重置为已取消
if p, err := s.repo.StrmSyncPath.FindByID(ctx, pathID); err == nil && p != nil {
if p.LastSyncStatus == model.StrmSyncRecordRunning {
p.LastSyncStatus = model.StrmSyncRecordCanceled
p.LastSyncMessage = "已取消"
_ = s.repo.StrmSyncPath.Update(ctx, p)
}
} }
cancel()
return nil return nil
} }
@@ -465,6 +477,8 @@ func (st *strmSyncState) walk115Flat(open115 *cloud115.OpenClient) error {
return fmt.Errorf("115: 获取文件列表失败:%w", err) return fmt.Errorf("115: 获取文件列表失败:%w", err)
} }
st.updateSyncMessage(fmt.Sprintf("正在拉取远端文件列表 (共 %d 个文件)...", totalCount))
allFiles := make([]cloud115.RemoteFile, 0, totalCount) allFiles := make([]cloud115.RemoteFile, 0, totalCount)
allFiles = append(allFiles, firstBatch...) allFiles = append(allFiles, firstBatch...)
@@ -492,7 +506,7 @@ func (st *strmSyncState) walk115Flat(open115 *cloud115.OpenClient) error {
} }
close(taskCh) close(taskCh)
workers := 4 workers := 8
if len(pageTasks) < workers { if len(pageTasks) < workers {
workers = len(pageTasks) workers = len(pageTasks)
} }
@@ -557,12 +571,16 @@ func (st *strmSyncState) walk115Flat(open115 *cloud115.OpenClient) error {
var ( var (
pwg sync.WaitGroup pwg sync.WaitGroup
dirWorkers = 4 dirWorkers = 8
doneDirs atomic.Int64
totalDirs = len(pidList)
) )
if len(pidList) < dirWorkers { if len(pidList) < dirWorkers {
dirWorkers = len(pidList) dirWorkers = len(pidList)
} }
st.updateSyncMessage(fmt.Sprintf("正在解析目录树 (0/%d)...", totalDirs))
for i := 0; i < dirWorkers; i++ { for i := 0; i < dirWorkers; i++ {
pwg.Add(1) pwg.Add(1)
go func() { go func() {
@@ -571,47 +589,55 @@ func (st *strmSyncState) walk115Flat(open115 *cloud115.OpenClient) error {
if ctx.Err() != nil { if ctx.Err() != nil {
return return
} }
if _, loaded := st.dirCache.Load(pid); loaded {
if n := doneDirs.Add(1); n%20 == 0 || n == int64(totalDirs) {
st.updateSyncMessage(fmt.Sprintf("正在解析目录树 (%d/%d)...", n, totalDirs))
}
continue
}
detail, err := open115.GetFsDetailByCid(ctx, pid) detail, err := open115.GetFsDetailByCid(ctx, pid)
if err != nil { if err != nil {
st.s.log.Warn("115: 获取目录详情失败", zap.String("pid", pid), zap.Error(err)) st.s.log.Warn("115: 获取目录详情失败", zap.String("pid", pid), zap.Error(err))
continue } else if detail != nil {
} // 解析相对路径
if detail == nil { relPath := detail.RelativePath(rootCID)
continue st.dirCache.Store(pid, relPath)
} _ = st.s.repo.StrmDirCache.Set(ctx, st.p.ID, pid, relPath)
// 解析相对路径
relPath := detail.RelativePath(rootCID)
st.dirCache.Store(pid, relPath)
_ = st.s.repo.StrmDirCache.Set(ctx, st.p.ID, pid, relPath)
// 顺便解析并缓存 detail.Paths 中包含的中间各层级目录 // 顺便解析并缓存 detail.Paths 中包含的中间各层级目录
for _, ancestor := range detail.Paths { for _, ancestor := range detail.Paths {
if ancestor.FileId == "0" || ancestor.FileId == rootCID { if ancestor.FileId == "0" || ancestor.FileId == rootCID {
continue continue
} }
if _, loaded := st.dirCache.Load(ancestor.FileId); !loaded { if _, loaded := st.dirCache.Load(ancestor.FileId); !loaded {
subDetail := &cloud115.RemoteFileDetail{ subDetail := &cloud115.RemoteFileDetail{
FileId: ancestor.FileId, FileId: ancestor.FileId,
FileName: ancestor.Name, FileName: ancestor.Name,
Paths: nil, Paths: nil,
} }
for _, p := range detail.Paths { for _, p := range detail.Paths {
subDetail.Paths = append(subDetail.Paths, p) subDetail.Paths = append(subDetail.Paths, p)
if p.FileId == ancestor.FileId { if p.FileId == ancestor.FileId {
break break
}
} }
ancestorRel := subDetail.RelativePath(rootCID)
st.dirCache.Store(ancestor.FileId, ancestorRel)
_ = st.s.repo.StrmDirCache.Set(ctx, st.p.ID, ancestor.FileId, ancestorRel)
} }
ancestorRel := subDetail.RelativePath(rootCID)
st.dirCache.Store(ancestor.FileId, ancestorRel)
_ = st.s.repo.StrmDirCache.Set(ctx, st.p.ID, ancestor.FileId, ancestorRel)
} }
} }
if n := doneDirs.Add(1); n%10 == 0 || n == int64(totalDirs) {
st.updateSyncMessage(fmt.Sprintf("正在解析目录树 (%d/%d)...", n, totalDirs))
}
} }
}() }()
} }
pwg.Wait() pwg.Wait()
} }
st.updateSyncMessage(fmt.Sprintf("正在生成 STRM 与同步文件 (共 %d 个)...", len(allFiles)))
// 5. 分类处理所有文件 // 5. 分类处理所有文件
for _, f := range allFiles { for _, f := range allFiles {
if ctx.Err() != nil { if ctx.Err() != nil {
@@ -1138,6 +1164,18 @@ func (st *strmSyncState) flushProgress() {
} }
} }
// updateSyncMessage 实时更新同步阶段提示信息,让前端界面清晰了解当前进度。
func (st *strmSyncState) updateSyncMessage(msg string) {
st.mu.Lock()
st.rec.Message = msg
st.p.LastSyncMessage = msg
rec := *st.rec
p := *st.p
st.mu.Unlock()
_ = st.s.repo.StrmSyncRecord.Update(st.ctx, &rec)
_ = st.s.repo.StrmSyncPath.Update(st.ctx, &p)
}
// ─── 定时同步巡检 ────────────────────────────────────────────────────────────── // ─── 定时同步巡检 ──────────────────────────────────────────────────────────────
func (s *StrmService) cronLoop(ctx context.Context) { func (s *StrmService) cronLoop(ctx context.Context) {