fix media supply citation attribution
Desktop Client Build / Resolve Build Metadata (push) Successful in 26s
Frontend CI / Frontend (push) Successful in 2m53s
Backend CI / Backend (push) Successful in 16m37s
Desktop Client Build / Build Desktop Client (push) Successful in 23m40s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 28s

This commit is contained in:
2026-06-25 23:53:35 +08:00
parent 89ef95b65d
commit 6780963c28
16 changed files with 1033 additions and 457 deletions
@@ -32,7 +32,78 @@ type monitoringAliasQuerier interface {
Query(ctx context.Context, sql string, args ...interface{}) (pgx.Rows, error)
}
func loadMonitoringPublishedAliasIndex(ctx context.Context, q monitoringAliasQuerier, tenantID int64) (monitoringPublishedAliasIndex, error) {
const (
monitoringContentSourcePublishedArticle = "published_article"
monitoringContentSourceManualMark = "manual_mark"
monitoringContentSourceMediaSupplyOrderItem = "media_supply_order_item"
monitoringMediaSupplySubmissionCitationLabel = "投稿引用"
)
func loadMonitoringMediaSupplyPublishedAliases(ctx context.Context, q monitoringAliasQuerier, tenantID int64) ([]monitoringPublishedAlias, error) {
if q == nil {
return nil, nil
}
rows, err := q.Query(ctx, `
SELECT
o.article_id,
i.id,
COALESCE(NULLIF(o.title, ''), NULLIF(i.resource_name_snapshot, ''), '未命名文章') AS article_title,
COALESCE(NULLIF(i.resource_name_snapshot, ''), $2) AS publish_platform,
i.external_article_url
FROM media_supply_order_items i
JOIN media_supply_orders o ON o.id = i.order_id
WHERE o.tenant_id = $1
AND o.deleted_at IS NULL
AND NULLIF(TRIM(COALESCE(i.external_article_url, '')), '') IS NOT NULL
ORDER BY i.updated_at DESC, i.id DESC
`, tenantID, monitoringMediaSupplySubmissionCitationLabel)
if err != nil {
return nil, responseInternalAliasLookupError()
}
defer rows.Close()
items := make([]monitoringPublishedAlias, 0)
for rows.Next() {
var articleID sql.NullInt64
var itemID int64
var articleTitle string
var publishPlatform string
var originalURL string
if scanErr := rows.Scan(
&articleID,
&itemID,
&articleTitle,
&publishPlatform,
&originalURL,
); scanErr != nil {
return nil, responseInternalAliasScanError()
}
normalizedURL := normalizeCitationURL(originalURL)
if normalizedURL == "" {
continue
}
item := monitoringPublishedAlias{
ArticleTitle: articleTitle,
PublishPlatform: publishPlatform,
OriginalURL: strings.TrimSpace(originalURL),
NormalizedURL: normalizedURL,
SourceType: monitoringContentSourceMediaSupplyOrderItem,
SourceID: itemID,
SourcePriority: 1,
}
if articleID.Valid {
item.ArticleID = articleID.Int64
}
items = append(items, item)
}
if err := rows.Err(); err != nil {
return nil, responseInternalAliasScanError()
}
return items, nil
}
func loadMonitoringPublishedAliasIndex(ctx context.Context, q monitoringAliasQuerier, businessQ monitoringAliasQuerier, tenantID int64) (monitoringPublishedAliasIndex, error) {
rows, err := q.Query(ctx, `
WITH alias_sources AS (
SELECT
@@ -42,11 +113,11 @@ func loadMonitoringPublishedAliasIndex(ctx context.Context, q monitoringAliasQue
COALESCE(publish_platform_name_snapshot, '已发布内容') AS publish_platform,
original_url,
normalized_url,
'published_article' AS source_type,
article_id AS source_id,
1 AS source_priority,
updated_at AS sort_updated_at,
id AS sort_id
'published_article' AS source_type,
article_id AS source_id,
2 AS source_priority,
updated_at AS sort_updated_at,
id AS sort_id
FROM monitoring_article_url_aliases
WHERE tenant_id = $1
AND confidence = 'high'
@@ -58,11 +129,11 @@ func loadMonitoringPublishedAliasIndex(ctx context.Context, q monitoringAliasQue
COALESCE(NULLIF(publish_platform, ''), '外部文章') AS publish_platform,
original_url,
normalized_url,
'manual_mark' AS source_type,
id AS source_id,
2 AS source_priority,
updated_at AS sort_updated_at,
id AS sort_id
'manual_mark' AS source_type,
id AS source_id,
3 AS source_priority,
updated_at AS sort_updated_at,
id AS sort_id
FROM monitoring_user_marked_articles
WHERE tenant_id = $1
AND expires_at > NOW()
@@ -74,9 +145,9 @@ func loadMonitoringPublishedAliasIndex(ctx context.Context, q monitoringAliasQue
publish_platform,
original_url,
normalized_url,
source_type,
source_id,
source_priority
source_type,
source_id,
source_priority
FROM alias_sources
ORDER BY source_priority ASC, sort_updated_at DESC, sort_id DESC
`, tenantID)
@@ -111,32 +182,44 @@ func loadMonitoringPublishedAliasIndex(ctx context.Context, q monitoringAliasQue
item.PublishRecordID = nullableInt64Value(publishRecordID)
item.OriginalURL = originalURL
item.NormalizedURL = normalizedURL
for _, key := range monitoringCitationCandidateKeys(originalURL, normalizedURL) {
existing, exists := index[key]
if exists && existing.SourcePriority < item.SourcePriority {
continue
}
if exists && item.SourcePriority < existing.SourcePriority {
index[key] = item
continue
}
if exists && (existing.SourceType != item.SourceType || existing.SourceID != item.SourceID) {
existing.Ambiguous = true
index[key] = existing
continue
}
if !exists {
index[key] = item
}
}
addMonitoringPublishedAliasToIndex(index, item)
}
if err := rows.Err(); err != nil {
return nil, responseInternalAliasScanError()
}
mediaAliases, err := loadMonitoringMediaSupplyPublishedAliases(ctx, businessQ, tenantID)
if err != nil {
return nil, err
}
for _, item := range mediaAliases {
addMonitoringPublishedAliasToIndex(index, item)
}
return index, nil
}
func addMonitoringPublishedAliasToIndex(index monitoringPublishedAliasIndex, item monitoringPublishedAlias) {
for _, key := range monitoringCitationCandidateKeys(item.OriginalURL, item.NormalizedURL) {
existing, exists := index[key]
if exists && existing.SourcePriority < item.SourcePriority {
continue
}
if exists && item.SourcePriority < existing.SourcePriority {
index[key] = item
continue
}
if exists && (existing.SourceType != item.SourceType || existing.SourceID != item.SourceID) {
existing.Ambiguous = true
index[key] = existing
continue
}
if !exists {
index[key] = item
}
}
}
func (index monitoringPublishedAliasIndex) Match(rawURL, normalizedURL string) (monitoringPublishedAlias, bool) {
for _, key := range monitoringCitationCandidateKeys(rawURL, normalizedURL) {
item, ok := index[key]