diff --git a/scripts/contentsync/main.go b/scripts/contentsync/main.go index 39d0a84..8fbaf87 100644 --- a/scripts/contentsync/main.go +++ b/scripts/contentsync/main.go @@ -261,8 +261,8 @@ func runPass(ctx context.Context, srcDB *mongo.Database, t target, spec CollSpec return out, c.Err() } - // localTwins 查出这批源文档在目标库的「同一集另一条」: - // (mediaId, hashId) 相同但 _id 不同。 + // localTwins 查出这批源文档在目标库的「同一集另一条」:同属一个 mediaId, + // 且 hashId 或 videoUrl 相同,但 _id 不同。 // // 各站点自己也在跑媒资库同步(common/media/syncMedia.go),它按 // (mediaId, hashId) 去重、用自己的 _id 为同一集插一条;本同步再把源库那条 @@ -270,29 +270,40 @@ func runPass(ctx context.Context, srcDB *mongo.Database, t target, spec CollSpec // 更要紧的是媒资库那条在 SaleCoin=0 时 listenPermission=2(免费), // 等于用户可以白看本该收费的集。 // + // 必须同时认 videoUrl:媒资库会给同一集重新发 hashId(实测 + // 「JK学姐不为人知的秘密」源库那条 hashId 6aa23652…、本地那条 + // 6acaf74c…,videoUrl 却一模一样),只比 hashId 会漏掉这一类。 + // // 以源库那条为准(购买记录都挂在它上面),把本地那条下架 isActive:false, // 不删除,便于事后回查。 localTwins := func(docs []bson.M) (map[any]any, error) { - hashes := make([]any, 0, len(docs)) - want := map[string]any{} // mediaId|hashId -> 源库 _id - for _, d := range docs { - h, ok := d["hashId"].(string) - if !ok || h == "" { - continue + keys := make([]any, 0, len(docs)*2) // 待查的 hashId 与 videoUrl + want := map[string]any{} // mediaId|key -> 源库 _id + add := func(mid primitive.ObjectID, v string, id any) { + if v == "" { + return } + keys = append(keys, v) + want[mid.Hex()+"|"+v] = id + } + for _, d := range docs { mid, ok := d["mediaId"].(primitive.ObjectID) if !ok { continue } - hashes = append(hashes, h) - want[mid.Hex()+"|"+h] = d["_id"] + h, _ := d["hashId"].(string) + u, _ := d["videoUrl"].(string) + add(mid, h, d["_id"]) + add(mid, u, d["_id"]) } - if len(hashes) == 0 { + if len(keys) == 0 { return nil, nil } - // hashId 上有索引,按它捞再在内存里配 mediaId - c, e := dst.Find(ctx, bson.M{"hashId": bson.M{"$in": hashes}, "isActive": true}, - options.Find().SetProjection(bson.M{"_id": 1, "mediaId": 1, "hashId": 1})) + // hashId 上有索引;videoUrl 没有,所以放在同一个 $or 里一次查完 + c, e := dst.Find(ctx, bson.M{"isActive": true, "$or": []bson.M{ + {"hashId": bson.M{"$in": keys}}, + {"videoUrl": bson.M{"$in": keys}}, + }}, options.Find().SetProjection(bson.M{"_id": 1, "mediaId": 1, "hashId": 1, "videoUrl": 1})) if e != nil { return nil, e } @@ -300,21 +311,28 @@ func runPass(ctx context.Context, srcDB *mongo.Database, t target, spec CollSpec out := map[any]any{} for c.Next(ctx) { var row struct { - ID primitive.ObjectID `bson:"_id"` - MediaID primitive.ObjectID `bson:"mediaId"` - HashID string `bson:"hashId"` + ID primitive.ObjectID `bson:"_id"` + MediaID primitive.ObjectID `bson:"mediaId"` + HashID string `bson:"hashId"` + VideoURL string `bson:"videoUrl"` } if e := c.Decode(&row); e != nil { return nil, e } - srcID, ok := want[row.MediaID.Hex()+"|"+row.HashID] - if !ok { - continue + for _, k := range []string{row.HashID, row.VideoURL} { + if k == "" { + continue + } + srcID, ok := want[row.MediaID.Hex()+"|"+k] + if !ok { + continue + } + if sid, ok := srcID.(primitive.ObjectID); ok && sid == row.ID { + continue // 就是源库那条自己,不是重复 + } + out[srcID] = row.ID + break } - if sid, ok := srcID.(primitive.ObjectID); ok && sid == row.ID { - continue // 就是源库那条自己,不是重复 - } - out[srcID] = row.ID } return out, c.Err() }