From 1145e0def68985eaa4e0e35acbf660ba81b20982 Mon Sep 17 00:00:00 2001 From: cybaka520 Date: Mon, 6 Jul 2026 10:55:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9Ettml=E4=BD=9C?= =?UTF-8?q?=E8=80=85github=E7=99=BB=E5=BD=95=E5=AD=97=E6=AE=B5=E6=94=AF?= =?UTF-8?q?=E6=8C=81=EF=BC=8C=E4=BC=98=E5=8C=96=E5=90=8C=E6=AD=A5=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../internal/infrastructure/meilisearch.go | 3 +- backend/internal/model/meilisearch.go | 33 ++++++++--------- backend/internal/service/search_service.go | 35 +++++++++++-------- worker/src/db/repository.rs | 12 +++---- worker/src/search/meilisearch.rs | 2 ++ worker/src/sync/diff.rs | 26 ++++++++++---- worker/src/worker/sync_task.rs | 1 + 7 files changed, 68 insertions(+), 44 deletions(-) diff --git a/backend/internal/infrastructure/meilisearch.go b/backend/internal/infrastructure/meilisearch.go index dba27cf..eb4349f 100644 --- a/backend/internal/infrastructure/meilisearch.go +++ b/backend/internal/infrastructure/meilisearch.go @@ -47,10 +47,11 @@ func EnsureMeiliSearchIndex(client *meilisearch.Client, indexName string) error "lyricText", "platformIds_ncm", "platformIds_qq", "platformIds_spotify", "platformIds_apple", "ttmlAuthorGithub", + "ttmlAuthorGithubLogin", } filterable := []string{ "platformIds_ncm", "platformIds_qq", "platformIds_spotify", "platformIds_apple", - "artists", "albums", "ttmlAuthorGithub", + "artists", "albums", "ttmlAuthorGithub", "ttmlAuthorGithubLogin", } sortable := []string{ "commitTimestamp", diff --git a/backend/internal/model/meilisearch.go b/backend/internal/model/meilisearch.go index 9815616..4750964 100644 --- a/backend/internal/model/meilisearch.go +++ b/backend/internal/model/meilisearch.go @@ -2,22 +2,23 @@ package model // MeiliSearch 索引文档结构(与 Rust Worker 写入保持一致) type MeiliSongDocument struct { - ID string `json:"id"` - MusicNames []string `json:"musicNames"` - MusicNamesPinyin []string `json:"musicNamesPinyin"` - Artists []string `json:"artists"` - ArtistsPinyin []string `json:"artistsPinyin"` - Albums []string `json:"albums"` - AlbumsPinyin []string `json:"albumsPinyin"` - LyricText string `json:"lyricText"` - PlatformIdsNcm []string `json:"platformIds_ncm,omitempty"` - PlatformIdsQq []string `json:"platformIds_qq,omitempty"` - PlatformIdsSpotify []string `json:"platformIds_spotify,omitempty"` - PlatformIdsApple []string `json:"platformIds_apple,omitempty"` - RawLyricFile string `json:"rawLyricFile"` - TtmlAuthorGithub string `json:"ttmlAuthorGithub,omitempty"` - WordCount int `json:"wordCount"` - LineCount int `json:"lineCount"` + ID string `json:"id"` + MusicNames []string `json:"musicNames"` + MusicNamesPinyin []string `json:"musicNamesPinyin"` + Artists []string `json:"artists"` + ArtistsPinyin []string `json:"artistsPinyin"` + Albums []string `json:"albums"` + AlbumsPinyin []string `json:"albumsPinyin"` + LyricText string `json:"lyricText"` + PlatformIdsNcm []string `json:"platformIds_ncm,omitempty"` + PlatformIdsQq []string `json:"platformIds_qq,omitempty"` + PlatformIdsSpotify []string `json:"platformIds_spotify,omitempty"` + PlatformIdsApple []string `json:"platformIds_apple,omitempty"` + RawLyricFile string `json:"rawLyricFile"` + TtmlAuthorGithub string `json:"ttmlAuthorGithub,omitempty"` + TtmlAuthorGithubLogin string `json:"ttmlAuthorGithubLogin,omitempty"` + WordCount int `json:"wordCount"` + LineCount int `json:"lineCount"` } // SearchHit MeiliSearch 搜索响应中单条命中(仅 Go 端关心的字段) diff --git a/backend/internal/service/search_service.go b/backend/internal/service/search_service.go index 5b4cc9f..fcc88e6 100644 --- a/backend/internal/service/search_service.go +++ b/backend/internal/service/search_service.go @@ -32,17 +32,18 @@ type SearchRequest struct { // SearchHitResult 单条命中 type SearchHitResult struct { - ID string `json:"id"` - MusicNames []string `json:"musicNames"` - Artists []string `json:"artists"` - Albums []string `json:"albums"` - PlatformIds map[string][]string `json:"platformIds"` - RawLyricFile string `json:"rawLyricFile"` - WordCount int `json:"wordCount"` - LineCount int `json:"lineCount"` - CommitTimestamp *int64 `json:"commitTimestamp,omitempty"` - LyricSnippet string `json:"lyricSnippet,omitempty"` // 歌词匹配片段(高亮) - TtmlAuthorGithub string `json:"ttmlAuthorGithub,omitempty"` + ID string `json:"id"` + MusicNames []string `json:"musicNames"` + Artists []string `json:"artists"` + Albums []string `json:"albums"` + PlatformIds map[string][]string `json:"platformIds"` + RawLyricFile string `json:"rawLyricFile"` + WordCount int `json:"wordCount"` + LineCount int `json:"lineCount"` + CommitTimestamp *int64 `json:"commitTimestamp,omitempty"` + LyricSnippet string `json:"lyricSnippet,omitempty"` // 歌词匹配片段(高亮) + TtmlAuthorGithub string `json:"ttmlAuthorGithub,omitempty"` + TtmlAuthorGithubLogin string `json:"ttmlAuthorGithubLogin,omitempty"` } // SearchResult 搜索结果 @@ -116,12 +117,13 @@ func (s *SearchService) Search(ctx context.Context, req SearchRequest) (*SearchR }, nil } -// searchByExactID 使用 filter 精确匹配所有平台 ID 字段 +// searchByExactID 使用 filter 精确匹配所有平台 ID 字段和投稿者 ID func (s *SearchService) searchByExactID(ctx context.Context, index *meilisearch.Index, req SearchRequest) (*SearchResult, error) { escaped := meiliEscape(req.Query) + // 支持:平台 ID(ncm/qq/spotify/apple) + 投稿者 GitHub ID + 投稿者 GitHub 用户名 filter := fmt.Sprintf( - `platformIds_ncm = "%s" OR platformIds_qq = "%s" OR platformIds_spotify = "%s" OR platformIds_apple = "%s"`, - escaped, escaped, escaped, escaped, + `platformIds_ncm = "%s" OR platformIds_qq = "%s" OR platformIds_spotify = "%s" OR platformIds_apple = "%s" OR ttmlAuthorGithub = "%s" OR ttmlAuthorGithubLogin = "%s"`, + escaped, escaped, escaped, escaped, escaped, escaped, ) req2 := meilisearch.SearchRequest{ Query: "", @@ -163,7 +165,7 @@ func searchOnFields(field string) []string { case "lyric": return []string{"lyricText"} case "author": - return []string{"ttmlAuthorGithub"} + return []string{"ttmlAuthorGithub", "ttmlAuthorGithubLogin"} case "", "all": return []string{ "musicNames", "musicNamesPinyin", @@ -197,6 +199,9 @@ func convertHit(raw interface{}) SearchHitResult { if v, ok := m["ttmlAuthorGithub"].(string); ok { hit.TtmlAuthorGithub = v } + if v, ok := m["ttmlAuthorGithubLogin"].(string); ok { + hit.TtmlAuthorGithubLogin = v + } if v := toStringSlice(m["platformIds_ncm"]); len(v) > 0 { hit.PlatformIds["ncm"] = v } diff --git a/worker/src/db/repository.rs b/worker/src/db/repository.rs index abefeff..3ccb042 100644 --- a/worker/src/db/repository.rs +++ b/worker/src/db/repository.rs @@ -28,7 +28,6 @@ pub struct SongUpsert { pub platform_mappings: Vec<(String, String)>, // (platform, platform_id) /// 从 raw_lyric_file 解析出的提交毫秒时间戳 pub commit_timestamp: Option, - /// 从时间戳转换而来的人类可读时间 pub commit_time: Option>, } @@ -42,18 +41,19 @@ impl Repository { Self { db } } - /// 查询本地已有的 raw_lyric_file 集合 - pub async fn list_raw_lyric_files(&self) -> anyhow::Result> { + /// 查询本地已有的 raw_lyric_file + /// 返回 HashMap + pub async fn list_raw_lyric_files(&self) -> anyhow::Result>> { tracing::debug!("repository.list_raw_lyric_files - 开始查询本地文件列表"); let rows = song::Entity::find() .select_only() - .column(song::Column::Id) .column(song::Column::RawLyricFile) - .into_tuple::<(i64, String)>() + .column(song::Column::CommitTimestamp) + .into_tuple::<(String, Option)>() .all(&self.db) .await?; tracing::debug!("repository.list_raw_lyric_files - 查询完成,共 {} 个文件", rows.len()); - Ok(rows.into_iter().map(|(id, f)| (f, id)).collect()) + Ok(rows.into_iter().collect()) } /// 通过 raw_lyric_file 查找 song id diff --git a/worker/src/search/meilisearch.rs b/worker/src/search/meilisearch.rs index 070f318..4ed0bfe 100644 --- a/worker/src/search/meilisearch.rs +++ b/worker/src/search/meilisearch.rs @@ -20,6 +20,7 @@ pub struct MeiliDocument { pub platform_ids_apple: Vec, pub raw_lyric_file: String, pub ttml_author_github: Option, + pub ttml_author_github_login: Option, pub word_count: i64, pub line_count: i64, /// 提交时间戳(毫秒),用于搜索结果按新旧排序 @@ -43,6 +44,7 @@ impl MeiliDocument { "platformIds_apple": self.platform_ids_apple, "rawLyricFile": self.raw_lyric_file, "ttmlAuthorGithub": self.ttml_author_github, + "ttmlAuthorGithubLogin": self.ttml_author_github_login, "wordCount": self.word_count, "lineCount": self.line_count, "commitTimestamp": self.commit_timestamp, diff --git a/worker/src/sync/diff.rs b/worker/src/sync/diff.rs index a73a73a..8123de0 100644 --- a/worker/src/sync/diff.rs +++ b/worker/src/sync/diff.rs @@ -13,9 +13,10 @@ pub struct Diff { /// 计算远程索引与本地已有 raw_lyric_file 的差异 /// /// - 远程有,本地无 -> to_add -/// - 远程有,本地有 -> to_update(简化处理:全部重新下载与更新;CC0 仓库以文件名唯一标识,文件名相同即视为同一首歌) +/// - 远程有,本地有,但 commit_timestamp 不同 -> to_update(只有时间戳不同才需要更新) +/// - 远程有,本地有,且 commit_timestamp 相同 -> 跳过(文件内容和 metadata 未变化) /// - 远程无,本地有 -> to_delete(当前 CC0 仓库不主动删除,留空实现预留) -pub fn compute_diff(remote: Vec, local: HashMap) -> Diff { +pub fn compute_diff(remote: Vec, local: HashMap>) -> Diff { let mut to_add = Vec::new(); let mut to_update = Vec::new(); let mut remote_seen: std::collections::HashSet = std::collections::HashSet::new(); @@ -24,10 +25,23 @@ pub fn compute_diff(remote: Vec, local: HashMap) -> Dif if let Some(raw) = entry.raw_file() { let raw = raw.to_string(); remote_seen.insert(raw.clone()); - if local.contains_key(&raw) { - to_update.push(entry); - } else { - to_add.push(entry); + + // 从文件名中提取 commit_timestamp,转换为 i64 以便比较 + let remote_ts = entry.parse_file_meta().map(|(ts, _)| ts as i64); + + match local.get(&raw) { + Some(local_ts) => { + // 本地有此文件,比较 commit_timestamp + if *local_ts != remote_ts { + // 时间戳不同,需要更新 + to_update.push(entry); + } + // 时间戳相同,跳过(不需要更新) + } + None => { + // 本地没有此文件,需要新增 + to_add.push(entry); + } } } } diff --git a/worker/src/worker/sync_task.rs b/worker/src/worker/sync_task.rs index 82d2d05..3d62c93 100644 --- a/worker/src/worker/sync_task.rs +++ b/worker/src/worker/sync_task.rs @@ -464,6 +464,7 @@ async fn process_one( .collect(), raw_lyric_file: d.raw_lyric_file.clone(), ttml_author_github: entry.ttml_author_github(), + ttml_author_github_login: entry.ttml_author_github_login(), word_count: parsed.word_count as i64, line_count: parsed.line_count as i64, commit_timestamp,