feat: restore Xiaohongshu read-only collection

This commit is contained in:
2026-09-14 20:05:32 +08:00
parent 44a28954cf
commit 4c37d0c9bf
13 changed files with 1539 additions and 39 deletions
+10 -32
View File
@@ -980,7 +980,7 @@ func verifyCreatorAccount(ctx context.Context, store *creator.Store, phaseAStore
if err != nil {
return creator.LoginResult{}, err
}
if account.Platform != creator.PlatformDouyin || profile.Platform != creator.PlatformDouyin || account.AuthorizationStatus != "authorized" || profile.PlatformAccountKey == "" || account.PlatformAccountKey != profile.PlatformAccountKey {
if account.Platform != profile.Platform || (account.Platform != creator.PlatformDouyin && account.Platform != creator.PlatformXiaohongshu) || account.AuthorizationStatus != "authorized" || profile.PlatformAccountKey == "" || account.PlatformAccountKey != profile.PlatformAccountKey {
return creator.LoginResult{}, creator.ErrConflict
}
environment, err := hubStore.GetEnvironmentContextForAccount(ctx, accountID)
@@ -994,8 +994,7 @@ func verifyCreatorAccount(ctx context.Context, store *creator.Store, phaseAStore
if err != nil {
return creator.LoginResult{}, fmt.Errorf("%w: gateway unavailable: %v", creator.ErrUnavailable, err)
}
browser := creatorGatewayBrowser{gateway: gateway, environment: environment}
uid, err := browser.Identity(ctx, profile.PlatformAccountKey)
uid, err := verifyCreatorPlatformIdentity(ctx, account.Platform, gateway, environment, profile.PlatformAccountKey)
if err != nil {
return creator.LoginResult{}, fmt.Errorf("%w: verify the manually logged-in browser identity: %v", creator.ErrConflict, err)
}
@@ -1126,8 +1125,8 @@ func syncCreatorCompetitorWithClaim(ctx context.Context, store *creator.Store, p
markErr := store.MarkCompetitorSync(ctx, competitorID, leaseToken, "blocked", "", blockErr.Error(), nil)
return creator.CollectionReport{}, errors.Join(blockErr, markErr)
}
if competitor.Platform != creator.PlatformDouyin {
return blocked(fmt.Errorf("%w: 小红书采集器尚未完成平台能力验证", creator.ErrUnavailable))
if competitor.Platform != creator.PlatformDouyin && competitor.Platform != creator.PlatformXiaohongshu {
return blocked(fmt.Errorf("%w: unsupported creator platform %s", creator.ErrUnavailable, competitor.Platform))
}
account, err := phaseAStore.GetAccount(ctx, accountID)
if err != nil {
@@ -1154,16 +1153,10 @@ func syncCreatorCompetitorWithClaim(ctx context.Context, store *creator.Store, p
if err != nil {
return blocked(fmt.Errorf("%w: gateway unavailable: %v", creator.ErrUnavailable, err))
}
browser := creatorGatewayBrowser{gateway: gateway, environment: environment}
if _, identityErr := browser.Identity(ctx, account.PlatformAccountKey); identityErr != nil {
return blocked(fmt.Errorf("%w: verify the manually logged-in browser identity: %v", creator.ErrConflict, identityErr))
}
collector := douyin.CreatorCollector{Browser: browser, AccountKey: competitor.PlatformAccountKey, SourceType: creator.SourceCompetitor, SourceID: competitor.ID}
canonicalSecUID, err := collector.CanonicalSecUID(ctx, account.PlatformAccountKey)
collector, _, err := newCreatorCollector(ctx, competitor.Platform, gateway, environment, account.PlatformAccountKey, creator.SourceCompetitor, competitor.ID)
if err != nil {
return blocked(fmt.Errorf("%w: account identity verification failed: %v", creator.ErrConflict, err))
}
collector.AccountKey = canonicalSecUID
collectionNow := now
if competitor.NextSyncAt != nil && !competitor.NextSyncAt.After(now) {
collectionNow = competitor.NextSyncAt.UTC()
@@ -1279,7 +1272,7 @@ func refreshCreatorMetricWork(ctx context.Context, store *creator.Store, phaseAS
if err != nil {
return err
}
if account.Platform != creator.PlatformDouyin || account.AuthorizationStatus != "authorized" || profile.BusinessStatus != "normal" || profile.LoginStatus != "logged_in" {
if (account.Platform != creator.PlatformDouyin && account.Platform != creator.PlatformXiaohongshu) || account.AuthorizationStatus != "authorized" || profile.BusinessStatus != "normal" || profile.LoginStatus != "logged_in" {
return creator.ErrConflict
}
environment, err := hubStore.GetEnvironmentContextForAccount(ctx, accountID)
@@ -1290,19 +1283,13 @@ func refreshCreatorMetricWork(ctx context.Context, store *creator.Store, phaseAS
if err != nil {
return fmt.Errorf("%w: gateway unavailable: %v", creator.ErrUnavailable, err)
}
browser := creatorGatewayBrowser{gateway: gateway, environment: environment}
if _, identityErr := browser.Identity(ctx, account.PlatformAccountKey); identityErr != nil {
return fmt.Errorf("%w: verify the manually logged-in browser identity: %v", creator.ErrConflict, identityErr)
}
collector := douyin.CreatorCollector{Browser: browser, AccountKey: account.PlatformAccountKey, SourceType: work.SourceType, SourceID: work.SourceID}
canonical, err := collector.CanonicalSecUID(ctx, account.PlatformAccountKey)
collector, collectionKey, err := newCreatorCollector(ctx, work.Platform, gateway, environment, account.PlatformAccountKey, work.SourceType, work.SourceID)
if err != nil {
return fmt.Errorf("%w: account identity verification failed: %v", creator.ErrConflict, err)
}
collector.AccountKey = canonical
cursor := ""
for page := 0; page < 100; page++ {
result, pageErr := collector.ListWorks(ctx, canonical, cursor)
result, pageErr := collector.ListWorks(ctx, collectionKey, cursor)
if pageErr != nil {
return pageErr
}
@@ -1335,7 +1322,7 @@ func syncCreatorOwned(ctx context.Context, store *creator.Store, phaseAStore *ph
if err != nil {
return err
}
if account.Platform != creator.PlatformDouyin || account.AuthorizationStatus != "authorized" {
if account.Platform != creator.PlatformDouyin && account.Platform != creator.PlatformXiaohongshu || account.AuthorizationStatus != "authorized" {
return creator.ErrConflict
}
profile, err := store.GetAccountProfile(ctx, accountID)
@@ -1349,9 +1336,6 @@ func syncCreatorOwned(ctx context.Context, store *creator.Store, phaseAStore *ph
if err != nil {
return err
}
blockOwned := func(blockErr error) error {
return errors.Join(blockErr, store.MarkCollectionBlocked(ctx, creator.SourceOwned, accountID, blockErr.Error(), now, settings.LookbackDays))
}
environment, err := hubStore.GetEnvironmentContextForAccount(ctx, accountID)
if err != nil {
return fmt.Errorf("%w: account environment unavailable: %v", creator.ErrUnavailable, err)
@@ -1363,10 +1347,6 @@ func syncCreatorOwned(ctx context.Context, store *creator.Store, phaseAStore *ph
if err != nil {
return fmt.Errorf("%w: gateway unavailable: %v", creator.ErrUnavailable, err)
}
browser := creatorGatewayBrowser{gateway: gateway, environment: environment}
if _, identityErr := browser.Identity(ctx, account.PlatformAccountKey); identityErr != nil {
return blockOwned(fmt.Errorf("%w: verify the manually logged-in browser identity: %v", creator.ErrConflict, identityErr))
}
syncLease, err := store.ClaimSourceSync(ctx, creator.SourceOwned, account.ID)
if err != nil {
return err
@@ -1376,13 +1356,11 @@ func syncCreatorOwned(ctx context.Context, store *creator.Store, phaseAStore *ph
logrus.WithError(releaseErr).WithField("account_id", account.ID).Warn("creator source sync lease release failed")
}
}()
collector := douyin.CreatorCollector{Browser: browser, AccountKey: account.PlatformAccountKey, SourceType: creator.SourceOwned, SourceID: account.ID}
canonicalSecUID, err := collector.CanonicalSecUID(ctx, account.PlatformAccountKey)
collector, _, err := newCreatorCollector(ctx, account.Platform, gateway, environment, account.PlatformAccountKey, creator.SourceOwned, account.ID)
if err != nil {
blockErr := store.MarkCollectionBlocked(ctx, creator.SourceOwned, account.ID, err.Error(), now, settings.LookbackDays)
return errors.Join(fmt.Errorf("%w: account identity verification failed: %v", creator.ErrConflict, err), blockErr)
}
collector.AccountKey = canonicalSecUID
_, collectionNow, windowErr := store.NextCollectionWindow(ctx, creator.SourceOwned, account.ID, now, time.Duration(settings.NewWorkIntervalSeconds)*time.Second, settings.LookbackDays)
if windowErr != nil {
return windowErr