Fix STRM branch retention
This commit is contained in:
1 parent
2417a0d36f
commit
a6277573dd
3 files changed
+174
-6
No files matched your search
@@ -67,6 +67,15 @@ type strmBranchDTO struct {
|
|||||||
Source string `json:"source,omitempty"`
|
Source string `json:"source,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type strmBranchPatchDTO struct {
|
||||||
|
ParentID *string `json:"parent_id"`
|
||||||
|
Path *string `json:"path"`
|
||||||
|
Recursive *bool `json:"recursive"`
|
||||||
|
RetentionDays *int `json:"retention_days"`
|
||||||
|
BranchType *string `json:"branch_type"`
|
||||||
|
Status *string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handler) listStrmTasks(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) listStrmTasks(w http.ResponseWriter, r *http.Request) {
|
||||||
if !ensureServiceReady(w, h.strm != nil) {
|
if !ensureServiceReady(w, h.strm != nil) {
|
||||||
return
|
return
|
||||||
@@ -292,14 +301,19 @@ func (h *Handler) updateStrmBranch(w http.ResponseWriter, r *http.Request) {
|
|||||||
writeErr(w, err)
|
writeErr(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var in strmBranchDTO
|
var in strmBranchPatchDTO
|
||||||
if err := decodeJSON(r, &in); err != nil {
|
if err := decodeJSON(r, &in); err != nil {
|
||||||
writeErr(w, err)
|
writeErr(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
branch := fromStrmBranchDTO(taskID, in)
|
updated, err := h.strm.UpdateBranch(r.Context(), taskID, branchID, strm.BranchPatch{
|
||||||
branch.ID = branchID
|
ParentID: in.ParentID,
|
||||||
updated, err := h.strm.UpdateBranch(r.Context(), branch)
|
Path: in.Path,
|
||||||
|
Recursive: in.Recursive,
|
||||||
|
RetentionDays: in.RetentionDays,
|
||||||
|
BranchType: in.BranchType,
|
||||||
|
Status: in.Status,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErr(w, err)
|
writeErr(w, err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package strm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"litepan/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUpdateBranchRetentionPreservesPathAndRefreshesExpiry(t *testing.T) {
|
||||||
|
svc, st := testService(t)
|
||||||
|
svc.branches = st.StrmBranches
|
||||||
|
ctx := t.Context()
|
||||||
|
|
||||||
|
accountID, err := st.Accounts.Create(ctx, &domain.Account{
|
||||||
|
Name: "测试账号",
|
||||||
|
DriverType: "localfs",
|
||||||
|
IsActive: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
taskID, err := st.StrmTasks.Create(ctx, &domain.StrmTask{
|
||||||
|
Name: "电视剧",
|
||||||
|
AccountID: accountID,
|
||||||
|
ParentID: "library",
|
||||||
|
Path: "/云影音/电视剧",
|
||||||
|
Status: domain.StrmStatusActive,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
branch, err := svc.CreateBranch(ctx, &domain.StrmBranch{
|
||||||
|
TaskID: taskID,
|
||||||
|
ParentID: "one-piece",
|
||||||
|
Path: "/云影音/电视剧/海贼王",
|
||||||
|
Recursive: true,
|
||||||
|
RetentionDays: 30,
|
||||||
|
BranchType: domain.StrmBranchTypeTemporary,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if branch.ExpiresAt.IsZero() {
|
||||||
|
t.Fatal("监控分支创建后应按保留天数设置过期时间")
|
||||||
|
}
|
||||||
|
|
||||||
|
days := 90
|
||||||
|
updated, err := svc.UpdateBranch(ctx, taskID, branch.ID, BranchPatch{RetentionDays: &days})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if updated.ParentID != branch.ParentID || updated.Path != branch.Path || updated.RelativePath != branch.RelativePath {
|
||||||
|
t.Fatalf("仅修改保留天数不应改变目录信息,更新前=%+v,更新后=%+v", branch, updated)
|
||||||
|
}
|
||||||
|
if !updated.Recursive || updated.BranchType != domain.StrmBranchTypeTemporary || updated.Source != "manual" {
|
||||||
|
t.Fatalf("仅修改保留天数不应改变其他分支属性: %+v", updated)
|
||||||
|
}
|
||||||
|
if updated.RetentionDays != days {
|
||||||
|
t.Fatalf("保留天数=%d,期望=%d", updated.RetentionDays, days)
|
||||||
|
}
|
||||||
|
expectedExpiry := branch.CreatedAt.Add(90 * 24 * time.Hour)
|
||||||
|
if !updated.ExpiresAt.Equal(expectedExpiry) {
|
||||||
|
t.Fatalf("过期时间=%v,期望按创建时间计算为 %v", updated.ExpiresAt, expectedExpiry)
|
||||||
|
}
|
||||||
|
|
||||||
|
permanent := 0
|
||||||
|
updated, err = svc.UpdateBranch(ctx, taskID, branch.ID, BranchPatch{RetentionDays: &permanent})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !updated.ExpiresAt.IsZero() {
|
||||||
|
t.Fatalf("永久保留应清空过期时间,实际=%v", updated.ExpiresAt)
|
||||||
|
}
|
||||||
|
if updated.Path != branch.Path {
|
||||||
|
t.Fatalf("改为永久保留后路径被改变为 %q", updated.Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNormalizeTemporaryBranchExpiryUsesCreatedAt(t *testing.T) {
|
||||||
|
createdAt := time.Date(2026, 1, 1, 8, 0, 0, 0, time.Local)
|
||||||
|
branch := &domain.StrmBranch{
|
||||||
|
RetentionDays: 90,
|
||||||
|
CreatedAt: createdAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := normalizeTemporaryBranchExpiry(branch, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expected := createdAt.Add(90 * 24 * time.Hour)
|
||||||
|
if !branch.ExpiresAt.Equal(expected) {
|
||||||
|
t.Fatalf("过期时间=%v,期望=%v", branch.ExpiresAt, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -608,6 +608,15 @@ func (s *Service) ListBranches(ctx context.Context, taskID int64) ([]*domain.Str
|
|||||||
return s.branches.ListByTask(ctx, taskID)
|
return s.branches.ListByTask(ctx, taskID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BranchPatch struct {
|
||||||
|
ParentID *string
|
||||||
|
Path *string
|
||||||
|
Recursive *bool
|
||||||
|
RetentionDays *int
|
||||||
|
BranchType *string
|
||||||
|
Status *string
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) CreateBranch(ctx context.Context, branch *domain.StrmBranch) (*domain.StrmBranch, error) {
|
func (s *Service) CreateBranch(ctx context.Context, branch *domain.StrmBranch) (*domain.StrmBranch, error) {
|
||||||
if s.branches == nil {
|
if s.branches == nil {
|
||||||
return nil, domain.Errf(domain.CodeNotImplement)
|
return nil, domain.Errf(domain.CodeNotImplement)
|
||||||
@@ -624,6 +633,9 @@ func (s *Service) CreateBranch(ctx context.Context, branch *domain.StrmBranch) (
|
|||||||
if branch.BranchType == domain.StrmBranchTypeBase {
|
if branch.BranchType == domain.StrmBranchTypeBase {
|
||||||
branch.Recursive = false
|
branch.Recursive = false
|
||||||
branch.RetentionDays = 0
|
branch.RetentionDays = 0
|
||||||
|
branch.ExpiresAt = time.Time{}
|
||||||
|
} else if err := normalizeTemporaryBranchExpiry(branch, false); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
if branch.Status == "" {
|
if branch.Status == "" {
|
||||||
branch.Status = "running"
|
branch.Status = "running"
|
||||||
@@ -638,17 +650,47 @@ func (s *Service) CreateBranch(ctx context.Context, branch *domain.StrmBranch) (
|
|||||||
return s.branches.Get(ctx, id)
|
return s.branches.Get(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) UpdateBranch(ctx context.Context, branch *domain.StrmBranch) (*domain.StrmBranch, error) {
|
func (s *Service) UpdateBranch(ctx context.Context, taskID, branchID int64, patch BranchPatch) (*domain.StrmBranch, error) {
|
||||||
if s.branches == nil {
|
if s.branches == nil {
|
||||||
return nil, domain.Errf(domain.CodeNotImplement)
|
return nil, domain.Errf(domain.CodeNotImplement)
|
||||||
}
|
}
|
||||||
task, err := s.repo.Get(ctx, branch.TaskID)
|
branch, err := s.branches.Get(ctx, branchID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if branch.TaskID != taskID {
|
||||||
|
return nil, domain.Errorf(domain.CodeNotFound, "STRM 分支不存在")
|
||||||
|
}
|
||||||
|
task, err := s.repo.Get(ctx, taskID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if patch.ParentID != nil {
|
||||||
|
branch.ParentID = *patch.ParentID
|
||||||
|
}
|
||||||
|
if patch.Path != nil {
|
||||||
|
branch.Path = *patch.Path
|
||||||
|
}
|
||||||
|
if patch.Recursive != nil {
|
||||||
|
branch.Recursive = *patch.Recursive
|
||||||
|
}
|
||||||
|
retentionChanged := patch.RetentionDays != nil
|
||||||
|
if retentionChanged {
|
||||||
|
branch.RetentionDays = *patch.RetentionDays
|
||||||
|
}
|
||||||
|
if patch.BranchType != nil {
|
||||||
|
branch.BranchType = *patch.BranchType
|
||||||
|
}
|
||||||
|
if patch.Status != nil {
|
||||||
|
branch.Status = *patch.Status
|
||||||
|
}
|
||||||
branch.RelativePath = branchRelativePath(task.Path, branch.Path)
|
branch.RelativePath = branchRelativePath(task.Path, branch.Path)
|
||||||
if branch.BranchType == domain.StrmBranchTypeBase {
|
if branch.BranchType == domain.StrmBranchTypeBase {
|
||||||
branch.Recursive = false
|
branch.Recursive = false
|
||||||
|
branch.RetentionDays = 0
|
||||||
|
branch.ExpiresAt = time.Time{}
|
||||||
|
} else if err := normalizeTemporaryBranchExpiry(branch, retentionChanged); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := s.branches.Update(ctx, branch); err != nil {
|
if err := s.branches.Update(ctx, branch); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -656,6 +698,24 @@ func (s *Service) UpdateBranch(ctx context.Context, branch *domain.StrmBranch) (
|
|||||||
return s.branches.Get(ctx, branch.ID)
|
return s.branches.Get(ctx, branch.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeTemporaryBranchExpiry(branch *domain.StrmBranch, reset bool) error {
|
||||||
|
if branch.RetentionDays < 0 || branch.RetentionDays > 3650 {
|
||||||
|
return domain.Errorf(domain.CodeValidation, "监控分支保留天数需为 0 到 3650")
|
||||||
|
}
|
||||||
|
if branch.RetentionDays == 0 {
|
||||||
|
branch.ExpiresAt = time.Time{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if reset || branch.ExpiresAt.IsZero() {
|
||||||
|
baseTime := branch.CreatedAt
|
||||||
|
if baseTime.IsZero() {
|
||||||
|
baseTime = time.Now()
|
||||||
|
}
|
||||||
|
branch.ExpiresAt = baseTime.Add(time.Duration(branch.RetentionDays) * 24 * time.Hour)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) DeleteBranch(ctx context.Context, id int64) error {
|
func (s *Service) DeleteBranch(ctx context.Context, id int64) error {
|
||||||
if s.branches == nil {
|
if s.branches == nil {
|
||||||
return domain.Errf(domain.CodeNotImplement)
|
return domain.Errf(domain.CodeNotImplement)
|
||||||
|
|||||||
Reference in new issue
Block a user