From 7bf8bf30306003c15178d6d5f697aa6c6905b75b Mon Sep 17 00:00:00 2001 From: root Date: Tue, 27 Oct 2026 10:55:29 +0800 Subject: [PATCH] =?UTF-8?q?sitemap=20=E6=92=AD=E6=94=BE=E9=A1=B5=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=86=99=E7=AC=AC=E4=B8=80=E9=9B=86=E5=9C=B0=E5=9D=80?= =?UTF-8?q?=EF=BC=8C=E9=81=BF=E5=85=8D=20/play/{slug}/=20301?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 取第一集的规则与 app 详情接口选集列表一致(集号、ID 升序;原创剧只算已加水印的剧集), 没有可播放剧集的短剧不再写入 sitemap。 Co-Authored-By: Claude Opus 5.5 --- app/skd/job/sitemap/data.go | 87 ++++++++++++++++++++++++++-- app/skd/job/sitemap/play_url_test.go | 17 ++++++ 2 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 app/skd/job/sitemap/play_url_test.go diff --git a/app/skd/job/sitemap/data.go b/app/skd/job/sitemap/data.go index 04b7839..77bb175 100644 --- a/app/skd/job/sitemap/data.go +++ b/app/skd/job/sitemap/data.go @@ -2,11 +2,14 @@ package sitemap import ( "context" + "strconv" "strings" "sync" "time" + "videoweb/pub/common/log" "videoweb/pub/common/utils" "videoweb/pub/models/playletcategorymod" + "videoweb/pub/models/playletchaptermod" "videoweb/pub/models/playletmod" "videoweb/pub/models/playlettagmod" "videoweb/pub/models/playletthememod" @@ -16,9 +19,10 @@ import ( ) type idWithTime struct { - Id int64 - Slug string - LastMod time.Time + Id int64 + Slug string + LastMod time.Time + IsOriginal bool // 仅短剧使用:原创剧只算已加水印的剧集 } type staticPageEntry struct { @@ -144,7 +148,7 @@ func getURL(sg *SitemapGenerator) { if urls := buildPlayletPathURLs(webAddr, listed.items, "/detail", "weekly", 0.7); len(urls) > 0 { _ = sg.writeSitemapFiles("sitemap-playletdetail", urls) } - if urls := buildPlayletPathURLs(webAddr, listed.items, "/play", "weekly", 0.6); len(urls) > 0 { + if urls := buildPlayletPlayURLs(webAddr, listed.items, firstPlayletChapterIDs(ctx, listed.items), "weekly", 0.6); len(urls) > 0 { _ = sg.writeSitemapFiles("sitemap-playletplay", urls) } } @@ -194,6 +198,78 @@ func buildPlayletPathURLs(webAddr string, items []idWithTime, prefix string, cha return urls } +// buildPlayletPlayURLs 播放页直接写第一集的规范地址 /play/{slug}/{chapterId}/, +// 避免 /play/{slug}/ 在 h5 再 301 一次;没有可播放剧集的短剧不写入(h5 会返回 404)。 +func buildPlayletPlayURLs(webAddr string, items []idWithTime, firstChapter map[int64]int64, changeFreq string, priority float32) []URL { + urls := make([]URL, 0, len(items)) + for _, item := range items { + chapterID := firstChapter[item.Id] + if chapterID <= 0 { + continue + } + lastMod := formatLastMod(item.LastMod) + if lastMod == "" { + lastMod = time.Now().Format(time.DateOnly) + } + urls = append(urls, URL{ + Loc: utils.JoinUrlPathKeepSlash(webAddr, entityPath("/play", item.Slug, item.Id)+strconv.FormatInt(chapterID, 10)+"/"), + LastMod: lastMod, + ChangeFreq: changeFreq, + Priority: priority, + }) + } + return urls +} + +// firstPlayletChapterIDs 取每部短剧的第一集 ID,规则与 app 详情接口的选集列表一致 +// (按集号、ID 升序;原创剧只算已加水印的剧集),h5 播放页跳转也取这一集。 +func firstPlayletChapterIDs(ctx context.Context, items []idWithTime) map[int64]int64 { + result := make(map[int64]int64, len(items)) + original := make(map[int64]bool, len(items)) + ids := make([]int64, 0, len(items)) + for _, item := range items { + if item.Id > 0 { + ids = append(ids, item.Id) + original[item.Id] = item.IsOriginal + } + } + const batch = 500 + for start := 0; start < len(ids); start += batch { + end := start + batch + if end > len(ids) { + end = len(ids) + } + list, err := playletchaptermod.FindManyPlayletChapter(ctx, bson.M{ + playletchaptermod.FN.PlayletChapter.PlayletId: bson.M{"$in": ids[start:end]}, + }, options.Find(). + SetSort(bson.D{ + {Key: playletchaptermod.FN.PlayletChapter.PlayletId, Value: 1}, + {Key: playletchaptermod.FN.PlayletChapter.CurrentEpisode, Value: 1}, + {Key: playletchaptermod.FN.PlayletChapter.Id, Value: 1}, + }). + SetProjection(bson.M{ + playletchaptermod.FN.PlayletChapter.Id: 1, + playletchaptermod.FN.PlayletChapter.PlayletId: 1, + playletchaptermod.FN.PlayletChapter.CurrentEpisode: 1, + playletchaptermod.FN.PlayletChapter.WatermarkVideoUrl: 1, + })) + if err != nil { + log.Error("sitemap 查询短剧第一集失败", log.E(err)) + continue + } + for _, ch := range list { + if ch == nil || ch.Id <= 0 || result[ch.PlayletId] > 0 { + continue + } + if original[ch.PlayletId] && ch.WatermarkVideoUrl == "" { + continue + } + result[ch.PlayletId] = ch.Id + } + } + return result +} + func formatLastMod(t time.Time) string { if t.IsZero() { return "" @@ -226,6 +302,7 @@ func getPlayletList(ctx context.Context) listedPlaylets { playletmod.FN.Playlet.ThemeIds: 1, playletmod.FN.Playlet.TagIds: 1, playletmod.FN.Playlet.CategoryIds: 1, + playletmod.FN.Playlet.IsOriginal: 1, })) for _, info := range list { if info == nil || info.Id <= 0 { @@ -235,7 +312,7 @@ func getPlayletList(ctx context.Context) listedPlaylets { if lastMod.IsZero() { lastMod = info.OnlineAt } - result.items = append(result.items, idWithTime{Id: info.Id, Slug: info.Slug, LastMod: lastMod}) + result.items = append(result.items, idWithTime{Id: info.Id, Slug: info.Slug, LastMod: lastMod, IsOriginal: info.IsOriginal}) addListedIDs(result.themeIDs, info.ThemeIds) addListedIDs(result.tagIDs, info.TagIds) addListedIDs(result.categoryIDs, info.CategoryIds) diff --git a/app/skd/job/sitemap/play_url_test.go b/app/skd/job/sitemap/play_url_test.go new file mode 100644 index 0000000..0f0e4bd --- /dev/null +++ b/app/skd/job/sitemap/play_url_test.go @@ -0,0 +1,17 @@ +package sitemap + +import "testing" + +func TestBuildPlayletPlayURLs(t *testing.T) { + items := []idWithTime{ + {Id: 12, Slug: "xionnhge"}, + {Id: 13, Slug: "nochapter"}, + } + urls := buildPlayletPlayURLs("https://hgaidj.net", items, map[int64]int64{12: 27939}, "weekly", 0.6) + if len(urls) != 1 { + t.Fatalf("got %d urls, want 1 (playlet without chapters must be skipped)", len(urls)) + } + if urls[0].Loc != "https://hgaidj.net/play/xionnhge/27939/" { + t.Errorf("Loc = %q", urls[0].Loc) + } +}