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
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:
@@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="_图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 800.3 239.55">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: url(#_未命名的渐变_117);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-2 {
|
||||||
|
fill: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-3 {
|
||||||
|
fill: url(#_未命名的渐变_117-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-4 {
|
||||||
|
fill: url(#_未命名的渐变_117-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-5 {
|
||||||
|
fill: url(#_未命名的渐变_117-2);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<linearGradient id="_未命名的渐变_117" data-name="未命名的渐变 117" x1="731.38" y1="39.67" x2="585.56" y2="185.48" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" stop-color="#1fbdff"/>
|
||||||
|
<stop offset=".21" stop-color="#1f95ff"/>
|
||||||
|
<stop offset=".39" stop-color="#1f7aff"/>
|
||||||
|
<stop offset=".5" stop-color="#1f70ff"/>
|
||||||
|
<stop offset=".56" stop-color="#346aff"/>
|
||||||
|
<stop offset=".68" stop-color="#5363ff"/>
|
||||||
|
<stop offset=".8" stop-color="#695dff"/>
|
||||||
|
<stop offset=".9" stop-color="#775aff"/>
|
||||||
|
<stop offset="1" stop-color="#7c59ff"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="_未命名的渐变_117-2" data-name="未命名的渐变 117" x1="602.95" y1="49.72" x2="563.39" y2="89.28" xlink:href="#_未命名的渐变_117"/>
|
||||||
|
<linearGradient id="_未命名的渐变_117-3" data-name="未命名的渐变 117" x1="602.95" y1="136.9" x2="563.39" y2="176.45" xlink:href="#_未命名的渐变_117"/>
|
||||||
|
<linearGradient id="_未命名的渐变_117-4" data-name="未命名的渐变 117" x1="541.8" y1="93.31" x2="502.24" y2="132.87" xlink:href="#_未命名的渐变_117"/>
|
||||||
|
</defs>
|
||||||
|
<g>
|
||||||
|
<path class="cls-1" d="M642.74,5.33c19.51,11.92,38.33,25.02,57.56,37.34,25.22,16.17,50.57,32.09,75.69,48.29,5.7,3.68,11.84,6.98,17.31,10.99,5.09,3.73,8.07,9,6.65,15.53-1.91,8.8-11.08,11.27-17.31,16.22l-143.3,85.89c-10.07,5.13-24.96,1.35-26.32-11.24-.56-5.14-.42-15.97,0-21.24.47-5.95,4.57-8.95,10.31-9.38,14.25-1.07,33.17,5.59,33.1-15.53-.02-5.97-.82-12.38-.32-18.44,1.07-12.74,11.92-10.52,21.39-10.6,1.44-.01,2.86.01,4.28,0,7.96-.07,17.64,1.16,20.53-8.41,1.04-3.45.97-18.74.55-22.82-.57-5.62-5.53-9.25-10.92-9.69-8.89-.72-18.69.53-27.68,0-3.38.31-6.37,2.01-7.16,5.45-.86,9.84.89,21-.08,30.68-.47,4.65-4.54,6.63-8.75,6.94-7.63.55-17.25.09-24.92-.33-3.89-.21-8,.62-10.07-3.77-1.17-2.49-.54-4.43-.54-6.81-.02-7.97-.75-16.92-.33-24.63.26-4.75,2.38-8.81,7.55-9.37,7.3-.79,25.75,1.65,31.2-1.1,3.5-1.77,4.71-4.82,4.97-8.57.53-7.82-.82-16.09-.35-23.98-3.74-16.21-24.22-7.61-35.74-10.41-4.09-.99-6.79-4.28-7.27-8.42-.9-7.71.58-16.98,0-24.85,1.42-6.76,5.64-11.42,12.51-12.71,7.25-1.36,11.68,1.42,17.49,4.97Z"/>
|
||||||
|
<rect class="cls-5" x="561.23" y="47.71" width="43.88" height="43.59" rx="7.13" ry="7.13"/>
|
||||||
|
<rect class="cls-4" x="561.23" y="134.88" width="43.88" height="43.59" rx="7.13" ry="7.13"/>
|
||||||
|
<rect class="cls-3" x="500.08" y="91.29" width="43.88" height="43.59" rx="7.13" ry="7.13"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="cls-2" d="M0,57.64l.46-.27c7.68-.44,15.43-1.77,22.99-3.16,29.23-5.4,57.7-14.17,85.16-25.32l.7.14,8.7,10.87c.31.39.14.65-.19.94-13.33,5.8-26.75,11.41-40.25,16.8l61.01.27.28,1c-.6,23.72.76,47.8,0,71.47-.01.43-.15.75-.18,1.11-.03.46.43.91-.2,1.26l-121.8.07-.02-61.2H.27l-.27-.27v-13.71ZM119.81,72.53H36.22v7.32h83.59v-7.32ZM119.81,91.73H36.22v7.13h83.59v-7.13ZM36.02,110.91v7.68h83.79v-7.68H36.02Z"/>
|
||||||
|
<path class="cls-2" d="M461.32,21.55v15.08l-.27.27h-25.88v16.27l23.78.27v14.62s-.28.32-.37.36c-6.58.03-13.26.28-19.85.1-1.07-.03-2.59-.58-3.56.08v16.36h23.69l.27.27v14.89h-23.96v16.45h28.9v15.44s-.29.51-.37.55l-72.51-.26c-.61.15-.25,2.26-1.13,2.49l-18.36.04-.19-76.94c-4.3.04-8.6-.05-12.9,0-.7,0-1.04.34-1.85.03-.19-.07-.31.09-.25-.29.76-.63,1.19-1.5,1.68-2.35,9.24-16.15,16.46-33.39,25.14-49.81.37-.69.71-1.45,1.26-2.03h18.57l-8.6,18.09,24.33.18-8.39-18.15c.18-.54.9-.12,1.34-.11,4.54.12,9.37.09,13.92,0,1.02-.02,2.6-.65,3.35.29l8.53,17.79h23.69ZM417.24,36.9h-26.71l.08,79.51,26.63.36.18-16.63h-23.69c-.1,0-.4.29-.64.18l.09-14.98c.04-.09.33-.37.36-.37h23.6l.27-.27v-16.17h-24.24c-.09,0-.3-.3-.28-.45l.36-14.63,23.98-.08v-16.45Z"/>
|
||||||
|
<path class="cls-2" d="M216.94,114.57h62.28c.06,0,.28.24.46.18v17.45l-.27.27h-81.4c-.33,0-.44-.72-.47-.99-.26-2.63.19-5.44.19-8.05.06-30.55.11-61.36-.07-91.89-.02-4.2-.41-8.86-.12-13.01.03-.41.04-1.36.65-1.36h18.47l.27.27v97.13Z"/>
|
||||||
|
<path class="cls-2" d="M332.91,22.28l.18-18.37.3-.43c.72.12,1.55-.2,2.17-.21,4.46-.03,9.05.17,13.55.19.48,0,1.54-.3,1.87.04l.32,18.78h12.16v14.53c0,.06-.24.28-.18.46h-12.16l-.09,30.88,12.07-3.94.75.22-.42,15.74-12.37,4.36-.17,36.38c-2.21,3.76-4.14,7.75-6.48,11.44-.19.3-.5.91-.82,1.01l-23.12-.26c-.19-.38-.02-.6.16-.93,1.2-2.24,3.04-4.7,4.39-6.95,2.67-4.42,5.27-8.89,7.86-13.35.4-7.12.17-14.28.23-21.43-1.93.18-15.14,5.42-15.8,4.93-.24-.18-.33-.31-.31-.62l.05-14.78.34-.39,15.73-5.31v-36.73l-.27-.27h-15.64v-14.99h15.73Z"/>
|
||||||
|
<path class="cls-2" d="M288.69,111.42l-10.73-49.68c-1.69-8.32-4.13-16.51-5.91-24.8-.12-.54-.44-1.88-.43-2.33.04-1.17,1.42-.44,2.12-.44,4.77,0,9.62.04,14.46,0,1.5-.01,2.03-.7,2.59,1.23,2.1,7.24,3.38,15.34,5.04,22.75,3.83,17.13,8.11,34.26,11.61,51.45.1.47.46,1.68.07,1.97l-18.81-.15Z"/>
|
||||||
|
<path class="cls-2" d="M177.61,111.46c-.64-.14-1.36.14-1.92.15-4.1.01-8.17-.03-12.25.04-.77.01-5.31.11-5.51-.18l-.07-.63c7.4-25.03,11.48-50.88,16.36-76.5.49-.8,2.75-.17,3.48-.18,2.08-.03,4.12-.01,6.2,0,2.63.02,5.23-.12,7.85-.17.84.27-.1,2.46-.21,3.03-3.24,18.04-6.5,36-9.78,54.01l-4.16,20.42Z"/>
|
||||||
|
<path class="cls-2" d="M246.12,3.45c.1.02.42.18.46.32.76,3.3,1.34,6.65,2.06,9.96,4.04,18.57,7.87,37.58,12.2,55.82l.37,3.71h-18.66l-6.08-30.93c-2.12-12.11-5.79-24.42-7.73-36.5-.07-.44-.56-2.38.19-2.38h17.19Z"/>
|
||||||
|
<path class="cls-2" d="M51.4,8.39c.23.93-.74,1.44-1.28,2.11-8.38,10.34-17.21,20.34-25.8,30.51l-22.08-.04-.23-.31c.24-.8,1.01-1.13,1.56-1.74,9.05-10.08,18.02-20.25,26.89-30.52h20.94Z"/>
|
||||||
|
<path class="cls-2" d="M129.04,16.35c6.55,7.97,13.06,16.05,19.68,23.93,0,.9-1.81.42-2.48.45-5.46.28-10.96.09-16.46.17l-.72.57-.1-.53c-.37-.05-.72.05-1.09-.11-.25-.11-2.68-3.02-3.11-3.52-7.85-9.15-15.2-18.73-23.13-27.82l-.28-1.09,20.68-.1.62.3c2.18,2.55,4.27,5.18,6.4,7.77Z"/>
|
||||||
|
<path class="cls-2" d="M62.92,3.55c.1-.58.89-.61,1.37-.65,5.5-.43,11.59.3,17.16.05,1.52.17,1.09,3.18,1.06,4.44-.18,8.19.11,16.48-.18,24.67-.03.92,0,1.88-.35,2.75-.38.47-10.46,2.05-11.95,2.32-1.34.24-5.67,1.41-6.64,1.23-.2-.04-.34-.09-.46-.28V3.55Z"/>
|
||||||
|
<g>
|
||||||
|
<path class="cls-2" d="M58.37,156.95l-3.17,12.69h-25.13l-2.92,11.84h21.36l-3.26,12.87h-21.36l-3.35,13.55h26.08l-3.17,12.87H2.01l15.87-63.82h40.49Z"/>
|
||||||
|
<path class="cls-2" d="M101.76,193.75l-6.69,27.02h-12.35v-4.37c-4.98,3.6-9.86,5.58-14.67,5.58-9.61,0-13.81-7.38-11.67-16.3,2.4-9.52,11.15-14.49,30.88-17.24,1.2-3.86.09-5.49-4.29-5.49s-9.01,1.89-14.32,4.63l-3.95-10.72c7.46-3.86,14.84-6.09,21.87-6.09,13.21,0,18.96,7.72,15.18,22.99ZM85.89,198.55c-9.86,1.29-14.32,4.63-15.35,8.84-.6,2.06.77,2.74,3.34,2.74,3.69,0,6.43-1.97,9.18-4.37l2.83-7.21Z"/>
|
||||||
|
<path class="cls-2" transform="translate(-211.41 0)" d="M315.83,214.68l8.23-8.66c3.6,3.43,7.21,4.72,10.46,4.72,4.63,0,7.46-2.14,8.23-5.32.6-2.14-3.34-3.17-7.29-5.4-5.83-1.97-12.18-6.35-9.95-15.18,2.14-8.49,10.46-14.07,21.19-14.07,8.32,0,13.04,3.52,16.81,6.69l-8.23,8.23c-2.83-2.4-5.83-3.69-8.49-3.69-4.03,0-6.78,1.89-7.55,4.98-.51,2.14,3.17,2.83,7.21,4.89,6.09,2.06,12.44,6,10.04,15.61-2.06,8.23-10.04,14.5-22.73,14.5-6.95,0-13.81-3.17-17.93-7.29Z"/>
|
||||||
|
<path class="cls-2" d="M146.44,238.44l4.46-11.24c.86.17,2.06.43,2.57.43,5.23,0,8.83-3.09,10.64-6.52l1.46-1.8-6.69-47.35h15.01l1.29,20.93c.34,4.46.26,9.01.09,13.81l1.03-1.37c2.14-4.63,4.37-9.18,6.52-13.81l10.64-19.56h14.75l-28.99,49.58c-7.89,12.52-14.15,18.01-25.13,18.01-3.77,0-5.66-.51-7.63-1.12Z"/>
|
||||||
|
<path class="cls-2" d="M242.16,156.95c14.07,0,24.87,5.66,20.76,22.22-3.6,14.41-16.3,20.41-29.76,20.41h-8.06l-5.23,21.19h-15.35l15.87-63.82h21.79ZM248.68,176.25c1.46-5.66-1.72-7.2-9.09-7.2h-6.86l-4.63,18.44h6.09c7.46,0,12.78-4.2,14.5-11.24Z"/>
|
||||||
|
<path class="cls-2" d="M266.51,201.47l7.29-29.51h15.27l-7.2,28.99c-1.72,6.78-.77,7.98,2.49,7.98,4.03,0,6.95-1.97,10.38-5.58l8.15-31.39h15.27l-12.09,48.8h-12.35l.51-6.69-.17.51c-5.23,4.8-10.21,7.38-16.3,7.38-11.24,0-14.32-8.32-11.24-20.5Z"/>
|
||||||
|
<path class="cls-2" d="M315.83,214.68l8.23-8.66c3.6,3.43,7.21,4.72,10.46,4.72,4.63,0,7.46-2.14,8.23-5.32.6-2.14-3.34-3.17-7.29-5.4-5.83-1.97-12.18-6.35-9.95-15.18,2.14-8.49,10.46-14.07,21.19-14.07,8.32,0,13.04,3.52,16.81,6.69l-8.23,8.23c-2.83-2.4-5.83-3.69-8.49-3.69-4.03,0-6.78,1.89-7.55,4.98-.51,2.14,3.17,2.83,7.21,4.89,6.09,2.06,12.44,6,10.04,15.61-2.06,8.23-10.04,14.5-22.73,14.5-6.95,0-13.81-3.17-17.93-7.29Z"/>
|
||||||
|
<path class="cls-2" d="M394.31,152.24l-4.03,16.47-3.26,8.15c4.38-3.26,9.69-6.09,15.7-6.09,11.24,0,14.32,8.32,11.24,20.5l-7.29,29.51h-15.27l7.2-28.99c1.63-6.69.77-7.98-2.49-7.98-4.03,0-6.78,2.06-10.46,4.89l-8.41,32.08h-15.18l17.07-68.53h15.18Z"/>
|
||||||
|
<path class="cls-2" d="M426.04,210.91c1.2-4.55,5.75-7.98,10.98-7.98,6.43,0,10.04,4.8,8.58,10.89-1.2,4.72-5.66,7.98-10.98,7.98-6.35,0-10.12-4.63-8.58-10.89ZM440.36,156.61h18.78l-3.95,14.24-10.29,27.1h-10.72l3.26-27.1,2.92-14.24Z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="cls-2" d="M477.32,5.77h-7.2v-2.53h17.41v2.53h-7.2v21.23h-3.01V5.77Z"/>
|
||||||
|
<path class="cls-2" d="M491.77,3.24h3.63l4.57,12.71c.55,1.65,1.1,3.31,1.69,4.96h.16c.55-1.65,1.07-3.31,1.62-4.96l4.54-12.71h3.63v23.76h-2.82v-13.16c0-2.04.23-4.93.39-7h-.13l-1.88,5.41-4.51,12.35h-2.01l-4.51-12.35-1.88-5.41h-.13c.13,2.07.36,4.96.36,7v13.16h-2.72V3.24Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 9.4 KiB |
@@ -273,7 +273,7 @@ const enUS = {
|
|||||||
trackingQuestion: {
|
trackingQuestion: {
|
||||||
title: 'Question Detail',
|
title: 'Question Detail',
|
||||||
description:
|
description:
|
||||||
'Inspect answer content, citation sources, citation analysis, and content citations for a single question.',
|
'Inspect answer content, citation sources, citation analysis, and answer citations for a single question.',
|
||||||
},
|
},
|
||||||
knowledge: {
|
knowledge: {
|
||||||
title: 'Knowledge Base',
|
title: 'Knowledge Base',
|
||||||
@@ -596,24 +596,25 @@ const enUS = {
|
|||||||
citationSourcesTitle: 'Citation Sources',
|
citationSourcesTitle: 'Citation Sources',
|
||||||
citationAnalysis: 'Citation Analysis',
|
citationAnalysis: 'Citation Analysis',
|
||||||
citationAnalysisTitle: 'Citation Analysis',
|
citationAnalysisTitle: 'Citation Analysis',
|
||||||
contentCitations: 'Content Citations',
|
contentCitations: 'Answer Citations',
|
||||||
contentCitationsTitle: 'Content Citations',
|
contentCitationsTitle: 'Answer Citations',
|
||||||
imitationAction: 'Imitation create',
|
imitationAction: 'Imitation create',
|
||||||
taskDebugTitle: 'Async Task Debug',
|
taskDebugTitle: 'Async Task Debug',
|
||||||
taskDebugHint:
|
taskDebugHint:
|
||||||
'For troubleshooting the callback -> queue -> worker flow only. The four main content sections above stay unchanged.',
|
'For troubleshooting the callback -> queue -> worker flow only. The four main content sections above stay unchanged.',
|
||||||
contentCitationBadge: 'Content Citation',
|
contentCitationBadge: 'Published Article Citation',
|
||||||
|
mediaSupplySubmissionCitation: 'Submission Citation',
|
||||||
manualMarkedArticle: 'External Article',
|
manualMarkedArticle: 'External Article',
|
||||||
markAsExternalArticle: 'Mark as external article',
|
markAsExternalArticle: 'Mark as external article',
|
||||||
markedArticleSaved: 'Marked as external article.',
|
markedArticleSaved: 'Marked as external article.',
|
||||||
noQuestions: 'No hot questions for the current filters',
|
noQuestions: 'No hot questions for the current filters',
|
||||||
noCitationRanking: 'No attributable citations in the current window',
|
noCitationRanking: 'No attributable citations in the current window',
|
||||||
noCitedArticles: 'No content citations in the current window',
|
noCitedArticles: 'No published article citations in the current window',
|
||||||
viewArticleContent: 'View article content',
|
viewArticleContent: 'View article content',
|
||||||
noAnswer: 'No sampled answer for this platform',
|
noAnswer: 'No sampled answer for this platform',
|
||||||
noCitations: 'No citations for this platform',
|
noCitations: 'No citations for this platform',
|
||||||
noCitationAnalysis: 'No citation analysis for this platform',
|
noCitationAnalysis: 'No citation analysis for this platform',
|
||||||
noContentCitations: 'No content citations for the active platform',
|
noContentCitations: 'No answer citations for the active platform',
|
||||||
noTaskDebug: 'No async task debug data for the current filters',
|
noTaskDebug: 'No async task debug data for the current filters',
|
||||||
noPlatformBreakdown: 'No platform data is available for the current filters',
|
noPlatformBreakdown: 'No platform data is available for the current filters',
|
||||||
selectedPlatformUnauthorizedTitle: 'Selected Platform Not Authorized',
|
selectedPlatformUnauthorizedTitle: 'Selected Platform Not Authorized',
|
||||||
@@ -641,7 +642,7 @@ const enUS = {
|
|||||||
samples: 'samples',
|
samples: 'samples',
|
||||||
citations: 'citations',
|
citations: 'citations',
|
||||||
citedArticleMeta: '{count} cited articles',
|
citedArticleMeta: '{count} cited articles',
|
||||||
contentCitationMeta: '{count} content references',
|
contentCitationMeta: '{count} published article references',
|
||||||
manual: 'Manual',
|
manual: 'Manual',
|
||||||
automatic: 'Automatic',
|
automatic: 'Automatic',
|
||||||
noChange: 'Flat',
|
noChange: 'Flat',
|
||||||
@@ -663,7 +664,7 @@ const enUS = {
|
|||||||
channelName: 'Channel',
|
channelName: 'Channel',
|
||||||
domain: 'Domain',
|
domain: 'Domain',
|
||||||
share: 'Share',
|
share: 'Share',
|
||||||
contentName: 'Content',
|
contentName: 'Article',
|
||||||
publishPlatform: 'Publish Platform',
|
publishPlatform: 'Publish Platform',
|
||||||
distinctSites: 'Distinct Sites',
|
distinctSites: 'Distinct Sites',
|
||||||
brandContent: 'Has Brand Content',
|
brandContent: 'Has Brand Content',
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ const zhCN = {
|
|||||||
},
|
},
|
||||||
trackingQuestion: {
|
trackingQuestion: {
|
||||||
title: '问题详情',
|
title: '问题详情',
|
||||||
description: '按问题查看大模型回答、引用来源、引用分析和内容引用。',
|
description: '按问题查看大模型回答、引用来源、引用分析和回答引用。',
|
||||||
},
|
},
|
||||||
knowledge: {
|
knowledge: {
|
||||||
title: '知识库',
|
title: '知识库',
|
||||||
@@ -556,8 +556,6 @@ const zhCN = {
|
|||||||
platformMatrixTitle: '平台对比矩阵',
|
platformMatrixTitle: '平台对比矩阵',
|
||||||
hotQuestions: 'Hot Questions',
|
hotQuestions: 'Hot Questions',
|
||||||
hotQuestionsTitle: '高频问题',
|
hotQuestionsTitle: '高频问题',
|
||||||
hotQuestionsHint:
|
|
||||||
'可选择全部关键词库(搜索词)或具体单个搜索词;选择单个搜索词时只展示并采集该搜索词。',
|
|
||||||
questionSearchPlaceholder: '搜索当前范围下的问题',
|
questionSearchPlaceholder: '搜索当前范围下的问题',
|
||||||
noQuestionSearchResults: '没有匹配的问题',
|
noQuestionSearchResults: '没有匹配的问题',
|
||||||
questionDetailEyebrow: 'Question Detail',
|
questionDetailEyebrow: 'Question Detail',
|
||||||
@@ -566,7 +564,7 @@ const zhCN = {
|
|||||||
backToQuestions: '返回问题列表',
|
backToQuestions: '返回问题列表',
|
||||||
citationRanking: 'Citation Ranking',
|
citationRanking: 'Citation Ranking',
|
||||||
citationRankingTitle: '引用排行',
|
citationRankingTitle: '引用排行',
|
||||||
citationRankingHint: '按发布外链和手动标记 URL 归因外部文章在模型引用来源中的出现次数与占比。',
|
citationRankingHint: '按发布外链、投稿外链和手动标记 URL 归因外部文章在模型引用来源中的出现次数与占比。',
|
||||||
citationWindow7: '近 7 天',
|
citationWindow7: '近 7 天',
|
||||||
citationWindow30: '近 30 天',
|
citationWindow30: '近 30 天',
|
||||||
citationWindowLabel: '近 {days} 天',
|
citationWindowLabel: '近 {days} 天',
|
||||||
@@ -577,23 +575,24 @@ const zhCN = {
|
|||||||
citationSourcesTitle: '引用来源',
|
citationSourcesTitle: '引用来源',
|
||||||
citationAnalysis: 'Citation Analysis',
|
citationAnalysis: 'Citation Analysis',
|
||||||
citationAnalysisTitle: '引用分析',
|
citationAnalysisTitle: '引用分析',
|
||||||
contentCitations: 'Content Citations',
|
contentCitations: '回答引用',
|
||||||
contentCitationsTitle: '内容引用',
|
contentCitationsTitle: '回答引用',
|
||||||
imitationAction: '仿写创作',
|
imitationAction: '仿写创作',
|
||||||
taskDebugTitle: '异步任务调试',
|
taskDebugTitle: '异步任务调试',
|
||||||
taskDebugHint: '仅用于排查 callback -> queue -> worker 的异步流转,不影响上方四块主内容。',
|
taskDebugHint: '仅用于排查 callback -> queue -> worker 的异步流转,不影响上方四块主内容。',
|
||||||
contentCitationBadge: '内容引用',
|
contentCitationBadge: '发文引用',
|
||||||
|
mediaSupplySubmissionCitation: '投稿引用',
|
||||||
manualMarkedArticle: '外部文章',
|
manualMarkedArticle: '外部文章',
|
||||||
markAsExternalArticle: '标记为外部文章',
|
markAsExternalArticle: '标记为外部文章',
|
||||||
markedArticleSaved: '已标记为外部文章',
|
markedArticleSaved: '已标记为外部文章',
|
||||||
noQuestions: '当前筛选条件下暂无高频问题',
|
noQuestions: '当前筛选条件下暂无高频问题',
|
||||||
noCitationRanking: '当前时间窗口内暂无可归因引用',
|
noCitationRanking: '当前时间窗口内暂无可归因引用',
|
||||||
noCitedArticles: '当前时间窗口内暂无内容引用',
|
noCitedArticles: '当前时间窗口内暂无发文引用',
|
||||||
viewArticleContent: '查看文章内容',
|
viewArticleContent: '查看文章内容',
|
||||||
noAnswer: '该平台暂无采样回答',
|
noAnswer: '该平台暂无采样回答',
|
||||||
noCitations: '该平台无引用来源',
|
noCitations: '该平台无引用来源',
|
||||||
noCitationAnalysis: '该平台暂无引用分析',
|
noCitationAnalysis: '该平台暂无引用分析',
|
||||||
noContentCitations: '当前平台暂无内容引用',
|
noContentCitations: '当前平台暂无回答引用',
|
||||||
noTaskDebug: '当前条件下暂无异步任务调试数据',
|
noTaskDebug: '当前条件下暂无异步任务调试数据',
|
||||||
noPlatformBreakdown: '当前筛选条件下暂无可展示的平台数据',
|
noPlatformBreakdown: '当前筛选条件下暂无可展示的平台数据',
|
||||||
selectedPlatformUnauthorizedTitle: '当前平台未授权',
|
selectedPlatformUnauthorizedTitle: '当前平台未授权',
|
||||||
@@ -620,7 +619,7 @@ const zhCN = {
|
|||||||
samples: '次采样',
|
samples: '次采样',
|
||||||
citations: '次引用',
|
citations: '次引用',
|
||||||
citedArticleMeta: '{count} 篇内容被引用',
|
citedArticleMeta: '{count} 篇内容被引用',
|
||||||
contentCitationMeta: '含 {count} 条内容引用',
|
contentCitationMeta: '含 {count} 条发文引用',
|
||||||
manual: '手动',
|
manual: '手动',
|
||||||
automatic: '自动',
|
automatic: '自动',
|
||||||
noChange: '持平',
|
noChange: '持平',
|
||||||
@@ -642,7 +641,7 @@ const zhCN = {
|
|||||||
channelName: '渠道名称',
|
channelName: '渠道名称',
|
||||||
domain: '来源域名',
|
domain: '来源域名',
|
||||||
share: '占比',
|
share: '占比',
|
||||||
contentName: '内容名称',
|
contentName: '文章名称',
|
||||||
publishPlatform: '发布平台',
|
publishPlatform: '发布平台',
|
||||||
distinctSites: '引用站点数',
|
distinctSites: '引用站点数',
|
||||||
brandContent: '含品牌内容',
|
brandContent: '含品牌内容',
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const columns = computed<TableColumnsType<MediaSupplyOrderDetail>>(() => [
|
|||||||
align: 'right',
|
align: 'right',
|
||||||
},
|
},
|
||||||
{ title: '扣款', key: 'wallet', width: 150 },
|
{ title: '扣款', key: 'wallet', width: 150 },
|
||||||
{ title: '媒体', key: 'items', width: 110, align: 'right' },
|
{ title: '媒体', key: 'items', width: 110 },
|
||||||
{ title: '回链', key: 'backlinks', width: 220 },
|
{ title: '回链', key: 'backlinks', width: 220 },
|
||||||
{ title: '外部单号', key: 'external_order_code', dataIndex: 'external_order_code', width: 170 },
|
{ title: '外部单号', key: 'external_order_code', dataIndex: 'external_order_code', width: 170 },
|
||||||
{ title: '创建时间', key: 'created_at', dataIndex: 'created_at', width: 170 },
|
{ title: '创建时间', key: 'created_at', dataIndex: 'created_at', width: 170 },
|
||||||
@@ -267,7 +267,7 @@ function formatOrderError(message?: string | null): string {
|
|||||||
onShowSizeChange: handleTableChange,
|
onShowSizeChange: handleTableChange,
|
||||||
}"
|
}"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
:scroll="{ x: 1580 }"
|
:scroll="{ x: 1630 }"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'title'">
|
<template v-if="column.key === 'title'">
|
||||||
@@ -475,7 +475,7 @@ function formatOrderError(message?: string | null): string {
|
|||||||
color: #475467 !important;
|
color: #475467 !important;
|
||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
border-bottom: 1px solid #e2e8f0 !important;
|
border-bottom: 1px solid #e2e8f0 !important;
|
||||||
text-align: center;
|
padding: 12px 16px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.orders-table-wrap :deep(.ant-table-tbody > tr > td) {
|
.orders-table-wrap :deep(.ant-table-tbody > tr > td) {
|
||||||
|
|||||||
@@ -802,9 +802,10 @@ function toResourceRow(item: CustomerSupplierMediaResource): MediaSupplyResource
|
|||||||
selectedRowKeys,
|
selectedRowKeys,
|
||||||
preserveSelectedRowKeys: true,
|
preserveSelectedRowKeys: true,
|
||||||
onChange: onSelectionChange,
|
onChange: onSelectionChange,
|
||||||
|
columnWidth: 60,
|
||||||
}"
|
}"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
:scroll="{ x: 1560 }"
|
:scroll="{ x: 1764 }"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'name'">
|
<template v-if="column.key === 'name'">
|
||||||
@@ -1424,12 +1425,12 @@ div.favorite-modal :deep(.ant-input::placeholder) {
|
|||||||
background: #f7f7f7;
|
background: #f7f7f7;
|
||||||
color: #666;
|
color: #666;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
text-align: center;
|
|
||||||
border-color: #dcdcdc;
|
border-color: #dcdcdc;
|
||||||
|
padding: 10px 14px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-supply-table-wrap :deep(.ant-table-tbody > tr > td) {
|
.media-supply-table-wrap :deep(.ant-table-tbody > tr > td) {
|
||||||
padding: 10px 14px;
|
padding: 10px 14px !important;
|
||||||
border-color: #dcdcdc;
|
border-color: #dcdcdc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import { ArrowLeftOutlined, BookOutlined, CopyOutlined } from '@ant-design/icons-vue'
|
import { ArrowLeftOutlined, BookOutlined, CopyOutlined } from '@ant-design/icons-vue'
|
||||||
import type {
|
import type {
|
||||||
MonitoringQuestionCitationAnalysisItem,
|
MonitoringQuestionCitationAnalysisItem,
|
||||||
MonitoringQuestionContentCitation,
|
|
||||||
MonitoringQuestionDetailCitation,
|
MonitoringQuestionDetailCitation,
|
||||||
MonitoringQuestionDetailPlatform,
|
MonitoringQuestionDetailPlatform,
|
||||||
MonitoringSourceItem,
|
MonitoringSourceItem,
|
||||||
@@ -199,10 +198,7 @@ const activeDeepSeekCitationReferenceLabels = computed<string[][]>(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const activeInlineContentCitations = computed<TrackingAnalyzedContentCitation[]>(() => {
|
const activeInlineContentCitations = computed<TrackingAnalyzedContentCitation[]>(() => {
|
||||||
return analyzeContentCitations(
|
return analyzeAnswerCitations(activePlatform.value)
|
||||||
activePlatform.value,
|
|
||||||
detailQuery.data.value?.content_citations ?? [],
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const shouldShowInlineContentCitations = computed(() => {
|
const shouldShowInlineContentCitations = computed(() => {
|
||||||
@@ -353,6 +349,12 @@ function isManualMarkedCitation(
|
|||||||
return citation?.source_type === 'manual_mark'
|
return citation?.source_type === 'manual_mark'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isMediaSupplySubmissionCitation(
|
||||||
|
citation: MonitoringQuestionDetailCitation | null | undefined,
|
||||||
|
): boolean {
|
||||||
|
return citation?.source_type === 'media_supply_order_item'
|
||||||
|
}
|
||||||
|
|
||||||
function canMarkCitationAsExternal(
|
function canMarkCitationAsExternal(
|
||||||
citation: MonitoringQuestionDetailCitation | null | undefined,
|
citation: MonitoringQuestionDetailCitation | null | undefined,
|
||||||
): boolean {
|
): boolean {
|
||||||
@@ -373,14 +375,22 @@ function isLinkedInlineCitationPlatform(platformId: string | null | undefined):
|
|||||||
return platformId === 'yuanbao' || platformId === 'qwen'
|
return platformId === 'yuanbao' || platformId === 'qwen'
|
||||||
}
|
}
|
||||||
|
|
||||||
function isKimiInlineCitationPlatform(platformId: string | null | undefined): boolean {
|
function isQwenInlineCitationPlatform(platformId: string | null | undefined): boolean {
|
||||||
return platformId === 'kimi'
|
return platformId === 'qwen'
|
||||||
|
}
|
||||||
|
|
||||||
|
function isYuanbaoInlineCitationPlatform(platformId: string | null | undefined): boolean {
|
||||||
|
return platformId === 'yuanbao'
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDeepSeekInlineCitationPlatform(platformId: string | null | undefined): boolean {
|
function isDeepSeekInlineCitationPlatform(platformId: string | null | undefined): boolean {
|
||||||
return platformId === 'deepseek'
|
return platformId === 'deepseek'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function supportsAnswerCitationTags(platformId: string | null | undefined): boolean {
|
||||||
|
return isLinkedInlineCitationPlatform(platformId) || isDeepSeekInlineCitationPlatform(platformId)
|
||||||
|
}
|
||||||
|
|
||||||
function createQwenSourceGroupPattern(): RegExp {
|
function createQwenSourceGroupPattern(): RegExp {
|
||||||
return /\[\[source_group_web_(\d+)\]\]/g
|
return /\[\[source_group_web_(\d+)\]\]/g
|
||||||
}
|
}
|
||||||
@@ -409,10 +419,6 @@ function createQwenSourceGroupStripPattern(): RegExp {
|
|||||||
return /\[\[\s*source_group_web_[^\[\]]*?\]\]/gi
|
return /\[\[\s*source_group_web_[^\[\]]*?\]\]/gi
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMarkdownLinkPattern(): RegExp {
|
|
||||||
return /\[([^\]\n]+)\]\((https?:\/\/[^)\s]+)\)/g
|
|
||||||
}
|
|
||||||
|
|
||||||
function looksLikeHtmlAnswer(value: string | null | undefined): boolean {
|
function looksLikeHtmlAnswer(value: string | null | undefined): boolean {
|
||||||
const normalized = String(value ?? '').trim()
|
const normalized = String(value ?? '').trim()
|
||||||
return /<\/?[a-z][^>]*>/i.test(normalized)
|
return /<\/?[a-z][^>]*>/i.test(normalized)
|
||||||
@@ -430,13 +436,21 @@ function parseHtmlFragment(content: string): HTMLElement | null {
|
|||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectInlineCitationIndexes(answerText: string): number[] {
|
function collectPlatformInlineCitationIndexes(
|
||||||
const matches = [
|
answerText: string,
|
||||||
...answerText.matchAll(createQwenSourceGroupPattern()),
|
platformId: string | null | undefined,
|
||||||
...answerText.matchAll(createYuanbaoCitationPattern()),
|
): number[] {
|
||||||
]
|
const pattern = isQwenInlineCitationPlatform(platformId)
|
||||||
|
? createQwenSourceGroupPattern()
|
||||||
|
: isYuanbaoInlineCitationPlatform(platformId)
|
||||||
|
? createYuanbaoCitationPattern()
|
||||||
|
: null
|
||||||
|
|
||||||
return matches
|
if (!pattern) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(answerText.matchAll(pattern))
|
||||||
.map((match) => Number(match[1]))
|
.map((match) => Number(match[1]))
|
||||||
.filter((value) => Number.isFinite(value) && value > 0)
|
.filter((value) => Number.isFinite(value) && value > 0)
|
||||||
}
|
}
|
||||||
@@ -464,48 +478,6 @@ function deriveCitationChannelName(url: string): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectKimiInlineCitationOccurrences(answerText: string): Array<{
|
|
||||||
url: string
|
|
||||||
label: string | null
|
|
||||||
citationCount: number
|
|
||||||
}> {
|
|
||||||
const counts = new Map<string, { label: string | null; citationCount: number }>()
|
|
||||||
const pattern = createMarkdownLinkPattern()
|
|
||||||
|
|
||||||
for (const match of answerText.matchAll(pattern)) {
|
|
||||||
const matchIndex = match.index ?? 0
|
|
||||||
if (matchIndex > 0 && answerText[matchIndex - 1] === '!') {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const label = String(match[1] ?? '').trim() || null
|
|
||||||
const url = normalizeInlineCitationUrl(match[2])
|
|
||||||
if (!url) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const existing = counts.get(url)
|
|
||||||
if (existing) {
|
|
||||||
counts.set(url, {
|
|
||||||
label: existing.label ?? label,
|
|
||||||
citationCount: existing.citationCount + 1,
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
counts.set(url, {
|
|
||||||
label,
|
|
||||||
citationCount: 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.from(counts.entries()).map(([url, value]) => ({
|
|
||||||
url,
|
|
||||||
label: value.label,
|
|
||||||
citationCount: value.citationCount,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildInlineCitationSourceLookup(
|
function buildInlineCitationSourceLookup(
|
||||||
platform: MonitoringQuestionDetailPlatform | null,
|
platform: MonitoringQuestionDetailPlatform | null,
|
||||||
): Map<string, MonitoringSourceItem> {
|
): Map<string, MonitoringSourceItem> {
|
||||||
@@ -1068,15 +1040,16 @@ function formatAnswerContent(platform: MonitoringQuestionDetailPlatform | null):
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const withInlineMarkers = isLinkedInlineCitationPlatform(platform?.ai_platform_id)
|
let withInlineMarkers = sanitized
|
||||||
? sanitized
|
if (isQwenInlineCitationPlatform(platform?.ai_platform_id)) {
|
||||||
.replace(createQwenSourceGroupPattern(), (_, sourceGroupIndex) => {
|
withInlineMarkers = sanitized.replace(createQwenSourceGroupPattern(), (_, sourceGroupIndex) => {
|
||||||
return buildInlineCitationMarker(Number(sourceGroupIndex), platform)
|
return buildInlineCitationMarker(Number(sourceGroupIndex), platform)
|
||||||
})
|
})
|
||||||
.replace(createYuanbaoCitationPattern(), (_, sourceGroupIndex) => {
|
} else if (isYuanbaoInlineCitationPlatform(platform?.ai_platform_id)) {
|
||||||
return buildInlineCitationMarker(Number(sourceGroupIndex), platform)
|
withInlineMarkers = sanitized.replace(createYuanbaoCitationPattern(), (_, sourceGroupIndex) => {
|
||||||
})
|
return buildInlineCitationMarker(Number(sourceGroupIndex), platform)
|
||||||
: sanitized
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const normalizedMarkdown = withInlineMarkers
|
const normalizedMarkdown = withInlineMarkers
|
||||||
.replace(createQwenSourceGroupStripPattern(), '')
|
.replace(createQwenSourceGroupStripPattern(), '')
|
||||||
@@ -1139,41 +1112,11 @@ function collectDeepSeekInlineCitationOccurrences(
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
function analyzeContentCitations(
|
function analyzeAnswerCitations(
|
||||||
platform: MonitoringQuestionDetailPlatform | null,
|
|
||||||
contentCitations: MonitoringQuestionContentCitation[],
|
|
||||||
): TrackingAnalyzedContentCitation[] {
|
|
||||||
const platformId = normalizeMonitoringPlatformId(platform?.ai_platform_id)
|
|
||||||
const serverItems = contentCitations
|
|
||||||
.filter((item) => normalizeMonitoringPlatformId(item.ai_platform_id) === platformId)
|
|
||||||
.sort((left, right) => {
|
|
||||||
if (right.citation_count !== left.citation_count) {
|
|
||||||
return right.citation_count - left.citation_count
|
|
||||||
}
|
|
||||||
return left.article_title.localeCompare(right.article_title, 'zh-CN')
|
|
||||||
})
|
|
||||||
.map((item) => ({
|
|
||||||
key: `server-${item.ai_platform_id}-${item.source_type}-${item.source_id}`,
|
|
||||||
contentName: item.article_title || item.source_url || '--',
|
|
||||||
channelName: item.publish_platform || deriveCitationChannelName(item.source_url),
|
|
||||||
citationCount: item.citation_count,
|
|
||||||
citationRate: item.citation_rate,
|
|
||||||
citedURL: item.source_url || null,
|
|
||||||
sourceType: item.source_type,
|
|
||||||
}))
|
|
||||||
|
|
||||||
if (serverItems.length > 0) {
|
|
||||||
return serverItems
|
|
||||||
}
|
|
||||||
|
|
||||||
return analyzeInlineContentCitations(platform)
|
|
||||||
}
|
|
||||||
|
|
||||||
function analyzeInlineContentCitations(
|
|
||||||
platform: MonitoringQuestionDetailPlatform | null,
|
platform: MonitoringQuestionDetailPlatform | null,
|
||||||
): TrackingAnalyzedContentCitation[] {
|
): TrackingAnalyzedContentCitation[] {
|
||||||
const answerText = String(platform?.answer_text ?? '')
|
const answerText = String(platform?.answer_text ?? '')
|
||||||
if (!answerText) {
|
if (!answerText || !supportsAnswerCitationTags(platform?.ai_platform_id)) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1188,36 +1131,6 @@ function analyzeInlineContentCitations(
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return occurrences
|
|
||||||
.sort((left, right) => {
|
|
||||||
if (right.citationCount !== left.citationCount) {
|
|
||||||
return right.citationCount - left.citationCount
|
|
||||||
}
|
|
||||||
return left.url.localeCompare(right.url, 'zh-CN')
|
|
||||||
})
|
|
||||||
.map((item) => ({
|
|
||||||
key: `deepseek-${item.url}`,
|
|
||||||
contentName: item.label || item.url,
|
|
||||||
channelName: item.siteName || deriveCitationChannelName(item.url),
|
|
||||||
citationCount: item.citationCount,
|
|
||||||
citationRate: item.citationCount / totalCitationCount,
|
|
||||||
citedURL: item.url,
|
|
||||||
sourceType: null,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isKimiInlineCitationPlatform(platform?.ai_platform_id)) {
|
|
||||||
const occurrences = collectKimiInlineCitationOccurrences(answerText)
|
|
||||||
if (!occurrences.length) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const totalCitationCount = occurrences.reduce((sum, item) => sum + item.citationCount, 0)
|
|
||||||
if (!totalCitationCount) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const inlineCitationLookup = buildInlineCitationSourceLookup(platform)
|
|
||||||
return occurrences
|
return occurrences
|
||||||
.sort((left, right) => {
|
.sort((left, right) => {
|
||||||
if (right.citationCount !== left.citationCount) {
|
if (right.citationCount !== left.citationCount) {
|
||||||
@@ -1226,13 +1139,10 @@ function analyzeInlineContentCitations(
|
|||||||
return left.url.localeCompare(right.url, 'zh-CN')
|
return left.url.localeCompare(right.url, 'zh-CN')
|
||||||
})
|
})
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
const source = inlineCitationLookup.get(item.url)
|
|
||||||
const contentName = source?.title?.trim() || item.label || item.url
|
|
||||||
const channelName = source?.site_name?.trim() || deriveCitationChannelName(item.url)
|
|
||||||
return {
|
return {
|
||||||
key: `kimi-${item.url}`,
|
key: `deepseek-${item.url}`,
|
||||||
contentName,
|
contentName: item.label || item.url,
|
||||||
channelName,
|
channelName: item.siteName || deriveCitationChannelName(item.url),
|
||||||
citationCount: item.citationCount,
|
citationCount: item.citationCount,
|
||||||
citationRate: item.citationCount / totalCitationCount,
|
citationRate: item.citationCount / totalCitationCount,
|
||||||
citedURL: item.url,
|
citedURL: item.url,
|
||||||
@@ -1241,11 +1151,10 @@ function analyzeInlineContentCitations(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isLinkedInlineCitationPlatform(platform?.ai_platform_id)) {
|
const sourceGroupIndexes = collectPlatformInlineCitationIndexes(
|
||||||
return []
|
answerText,
|
||||||
}
|
platform?.ai_platform_id,
|
||||||
|
)
|
||||||
const sourceGroupIndexes = collectInlineCitationIndexes(answerText)
|
|
||||||
if (!sourceGroupIndexes.length) {
|
if (!sourceGroupIndexes.length) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -1557,39 +1466,49 @@ const contentCitationColumns = computed(() => [
|
|||||||
@click="openExternalURL(citation.cited_url)"
|
@click="openExternalURL(citation.cited_url)"
|
||||||
@keydown.enter="openExternalURL(citation.cited_url)"
|
@keydown.enter="openExternalURL(citation.cited_url)"
|
||||||
>
|
>
|
||||||
<div class="tracking-question-source-card__headline">
|
<div class="tracking-question-source-card__header">
|
||||||
<div class="tracking-question-source-card__indexes">
|
<div class="tracking-question-source-card__header-left">
|
||||||
<span
|
<div class="tracking-question-source-card__indexes">
|
||||||
v-for="label in resolveCitationIndexDisplayLabels(index, activePlatform)"
|
<span
|
||||||
:key="`${citation.cited_url}-${label}`"
|
v-for="label in resolveCitationIndexDisplayLabels(index, activePlatform)"
|
||||||
class="tracking-question-source-card__reference-badge"
|
:key="`${citation.cited_url}-${label}`"
|
||||||
|
class="tracking-question-source-card__reference-badge"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="shouldShowCitationSourceListIndex(index, activePlatform)"
|
||||||
|
class="tracking-question-source-card__index"
|
||||||
|
:class="{
|
||||||
|
'tracking-question-source-card__index--secondary':
|
||||||
|
resolveCitationIndexDisplayLabels(index, activePlatform).length > 0,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ index + 1 }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<strong class="tracking-question-source-card__site-name">{{ citation.site_name }}</strong>
|
||||||
|
<a-tag
|
||||||
|
v-if="isMediaSupplySubmissionCitation(citation)"
|
||||||
|
color="cyan"
|
||||||
|
class="tracking-question-source-card__badge"
|
||||||
>
|
>
|
||||||
{{ label }}
|
{{ t('tracking.mediaSupplySubmissionCitation') }}
|
||||||
</span>
|
</a-tag>
|
||||||
<span
|
<a-tag
|
||||||
v-if="shouldShowCitationSourceListIndex(index, activePlatform)"
|
v-else-if="citation.article_id"
|
||||||
class="tracking-question-source-card__index"
|
class="tracking-question-source-card__badge tracking-question-source-card__badge--published"
|
||||||
:class="{
|
|
||||||
'tracking-question-source-card__index--secondary':
|
|
||||||
resolveCitationIndexDisplayLabels(index, activePlatform).length > 0,
|
|
||||||
}"
|
|
||||||
>
|
>
|
||||||
[{{ index + 1 }}]
|
{{ t('tracking.contentCitationBadge') }}
|
||||||
</span>
|
</a-tag>
|
||||||
|
<a-tag
|
||||||
|
v-else-if="isManualMarkedCitation(citation)"
|
||||||
|
color="green"
|
||||||
|
class="tracking-question-source-card__badge"
|
||||||
|
>
|
||||||
|
{{ t('tracking.manualMarkedArticle') }}
|
||||||
|
</a-tag>
|
||||||
</div>
|
</div>
|
||||||
<strong>{{ citation.site_name }}</strong>
|
|
||||||
<span
|
|
||||||
v-if="resolveCitationTitle(citation)"
|
|
||||||
class="tracking-question-source-card__divider"
|
|
||||||
>
|
|
||||||
·
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="tracking-question-source-card__title"
|
|
||||||
:title="resolveCitationTitle(citation)"
|
|
||||||
>
|
|
||||||
{{ resolveCitationTitle(citation) }}
|
|
||||||
</span>
|
|
||||||
<div class="tracking-question-source-card__actions">
|
<div class="tracking-question-source-card__actions">
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
v-if="canMarkCitationAsExternal(citation)"
|
v-if="canMarkCitationAsExternal(citation)"
|
||||||
@@ -1620,20 +1539,13 @@ const contentCitationColumns = computed(() => [
|
|||||||
</button>
|
</button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<a-tag
|
</div>
|
||||||
v-if="citation.article_id"
|
<div
|
||||||
color="blue"
|
v-if="resolveCitationTitle(citation)"
|
||||||
class="tracking-question-source-card__badge"
|
class="tracking-question-source-card__title"
|
||||||
>
|
:title="resolveCitationTitle(citation)"
|
||||||
{{ t('tracking.contentCitationBadge') }}
|
>
|
||||||
</a-tag>
|
{{ resolveCitationTitle(citation) }}
|
||||||
<a-tag
|
|
||||||
v-else-if="isManualMarkedCitation(citation)"
|
|
||||||
color="green"
|
|
||||||
class="tracking-question-source-card__badge"
|
|
||||||
>
|
|
||||||
{{ t('tracking.manualMarkedArticle') }}
|
|
||||||
</a-tag>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tracking-question-source-card__link">
|
<div class="tracking-question-source-card__link">
|
||||||
{{ citation.cited_url }}
|
{{ citation.cited_url }}
|
||||||
@@ -1711,7 +1623,7 @@ const contentCitationColumns = computed(() => [
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Content Citations -->
|
<!-- Answer Citations -->
|
||||||
<section v-if="shouldShowInlineContentCitations" class="tracking-question-card">
|
<section v-if="shouldShowInlineContentCitations" class="tracking-question-card">
|
||||||
<div class="tracking-question-card__header">
|
<div class="tracking-question-card__header">
|
||||||
<div class="tracking-question-card__section-title">
|
<div class="tracking-question-card__section-title">
|
||||||
@@ -1747,6 +1659,12 @@ const contentCitationColumns = computed(() => [
|
|||||||
<small v-if="record.sourceType === 'manual_mark'" class="meta-subtext">
|
<small v-if="record.sourceType === 'manual_mark'" class="meta-subtext">
|
||||||
{{ t('tracking.manualMarkedArticle') }}
|
{{ t('tracking.manualMarkedArticle') }}
|
||||||
</small>
|
</small>
|
||||||
|
<small
|
||||||
|
v-else-if="record.sourceType === 'media_supply_order_item'"
|
||||||
|
class="meta-subtext"
|
||||||
|
>
|
||||||
|
{{ t('tracking.mediaSupplySubmissionCitation') }}
|
||||||
|
</small>
|
||||||
</template>
|
</template>
|
||||||
<strong v-else class="table-text" :title="record.contentName">
|
<strong v-else class="table-text" :title="record.contentName">
|
||||||
{{ record.contentName }}
|
{{ record.contentName }}
|
||||||
@@ -1805,11 +1723,24 @@ const contentCitationColumns = computed(() => [
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
padding: 24px 32px;
|
padding: 28px 32px;
|
||||||
background: #ffffff;
|
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
|
||||||
border: 1px solid #e8edf5;
|
border: 1px solid #e2e8f0;
|
||||||
border-radius: 20px;
|
border-radius: 16px;
|
||||||
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.02);
|
box-shadow: 0 4px 20px -2px rgba(15, 23, 42, 0.02);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-question-page__hero::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -50%;
|
||||||
|
right: -10%;
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
background: radial-gradient(circle, rgba(59, 130, 246, 0.03) 0%, rgba(255, 255, 255, 0) 70%);
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-page__header-top {
|
.tracking-question-page__header-top {
|
||||||
@@ -1820,36 +1751,38 @@ const contentCitationColumns = computed(() => [
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 6px;
|
gap: 8px;
|
||||||
height: 32px;
|
height: 36px;
|
||||||
padding: 0 12px 0 8px;
|
padding: 0 16px 0 12px;
|
||||||
border: none;
|
border: 1px solid #e2e8f0;
|
||||||
border-radius: 6px;
|
border-radius: 8px;
|
||||||
background: transparent;
|
background: #ffffff;
|
||||||
color: #64748b;
|
color: #475569;
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
margin-left: -8px;
|
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.02);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-page__back:hover {
|
.tracking-question-page__back:hover {
|
||||||
background: #e2e8f0;
|
border-color: #cbd5e1;
|
||||||
color: #0f172a;
|
color: #0f172a;
|
||||||
|
background: #f8fafc;
|
||||||
|
transform: translateX(-2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-page__copy {
|
.tracking-question-page__copy {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 6px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-page__eyebrow {
|
.tracking-question-page__eyebrow {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: #2563eb;
|
color: #2563eb;
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
letter-spacing: 0.1em;
|
letter-spacing: 0.1em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@@ -1858,40 +1791,59 @@ const contentCitationColumns = computed(() => [
|
|||||||
.tracking-question-page__copy h1 {
|
.tracking-question-page__copy h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: #0f172a;
|
color: #0f172a;
|
||||||
font-size: 32px;
|
font-size: 26px;
|
||||||
font-weight: 800;
|
font-weight: 700;
|
||||||
line-height: 1.25;
|
line-height: 1.3;
|
||||||
letter-spacing: -0.02em;
|
letter-spacing: -0.01em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-page__meta {
|
.tracking-question-page__meta {
|
||||||
margin: 8px 0 0;
|
margin: 4px 0 0;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 6px;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
background: #f1f5f9;
|
background: #f1f5f9;
|
||||||
border-radius: 6px;
|
border-radius: 8px;
|
||||||
color: #475569;
|
color: #475569;
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-page__alert {
|
.tracking-question-page__alert {
|
||||||
border-radius: 16px;
|
border-radius: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-page__tabs-wrapper {
|
.tracking-question-page__tabs-wrapper {
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
padding: 4px 24px 0;
|
padding: 0 16px;
|
||||||
border-radius: 16px;
|
border-radius: 12px;
|
||||||
border: 1px solid #e8edf5;
|
border: 1px solid #e2e8f0;
|
||||||
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.02);
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.02);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-tabs :deep(.ant-tabs-nav) {
|
.tracking-question-tabs :deep(.ant-tabs-nav) {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-question-tabs :deep(.ant-tabs-tab) {
|
||||||
|
padding: 14px 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #475569;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-question-tabs :deep(.ant-tabs-tab-active) {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-question-tabs :deep(.ant-tabs-ink-bar) {
|
||||||
|
height: 3px;
|
||||||
|
border-radius: 3px 3px 0 0;
|
||||||
|
background: #2563eb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-tabs .is-muted {
|
.tracking-question-tabs .is-muted {
|
||||||
@@ -1901,8 +1853,10 @@ const contentCitationColumns = computed(() => [
|
|||||||
.tracking-question-grid {
|
.tracking-question-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
|
grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
|
||||||
grid-auto-rows: 800px;
|
grid-auto-rows: calc(100vh - 280px);
|
||||||
gap: 20px;
|
min-height: 550px;
|
||||||
|
max-height: 750px;
|
||||||
|
gap: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-card {
|
.tracking-question-card {
|
||||||
@@ -1910,10 +1864,10 @@ const contentCitationColumns = computed(() => [
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 24px 28px;
|
padding: 24px 28px;
|
||||||
border: 1px solid #e8edf5;
|
border: 1px solid rgba(226, 232, 240, 0.8);
|
||||||
border-radius: 20px;
|
border-radius: 16px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.03);
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.02), 0 10px 15px -3px rgba(0, 0, 0, 0.03), 0 0 0 1px rgba(0, 0, 0, 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-card--answer {
|
.tracking-question-card--answer {
|
||||||
@@ -1939,17 +1893,17 @@ const contentCitationColumns = computed(() => [
|
|||||||
.tracking-question-card__section-title {
|
.tracking-question-card__section-title {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 14px;
|
gap: 10px;
|
||||||
color: #111827;
|
color: #0f172a;
|
||||||
font-size: 20px;
|
font-size: 16px;
|
||||||
font-weight: 700;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-card__accent {
|
.tracking-question-card__accent {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 6px;
|
width: 4px;
|
||||||
height: 28px;
|
height: 16px;
|
||||||
border-radius: 999px;
|
border-radius: 2px;
|
||||||
background: #2563eb;
|
background: #2563eb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2093,7 +2047,9 @@ const contentCitationColumns = computed(() => [
|
|||||||
|
|
||||||
.tracking-question-answer__headline h2 {
|
.tracking-question-answer__headline h2 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 22px;
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #334155;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-answer__meta {
|
.tracking-question-answer__meta {
|
||||||
@@ -2102,15 +2058,16 @@ const contentCitationColumns = computed(() => [
|
|||||||
gap: 18px;
|
gap: 18px;
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
padding-top: 18px;
|
padding-top: 18px;
|
||||||
color: #9ca3af;
|
color: #94a3b8;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
border-top: 1px dashed #f1f5f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-source-list,
|
.tracking-question-source-list,
|
||||||
.tracking-question-content-list {
|
.tracking-question-content-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 14px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-source-card,
|
.tracking-question-source-card,
|
||||||
@@ -2118,43 +2075,61 @@ const contentCitationColumns = computed(() => [
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 6px;
|
gap: 8px;
|
||||||
padding: 12px 16px;
|
padding: 16px;
|
||||||
|
padding-right: 76px;
|
||||||
border: 1px solid #edf2f7;
|
border: 1px solid #edf2f7;
|
||||||
border-radius: 10px;
|
border-radius: 12px;
|
||||||
background: #f8fbff;
|
background: #ffffff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.02);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-source-card:hover,
|
.tracking-question-source-card:hover,
|
||||||
.tracking-question-content-card:hover {
|
.tracking-question-content-card:hover {
|
||||||
border-color: #dbeafe;
|
border-color: #3b82f6;
|
||||||
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.05);
|
background: #f8fafc;
|
||||||
}
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 12px 20px -8px rgba(59, 130, 246, 0.08), 0 4px 6px -2px rgba(59, 130, 246, 0.02);
|
||||||
.tracking-question-source-card {
|
|
||||||
padding-right: 58px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-source-card.is-linked {
|
.tracking-question-source-card.is-linked {
|
||||||
border-color: #93c5fd;
|
border-color: #3b82f6;
|
||||||
background: #eef6ff;
|
background: #eff6ff;
|
||||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12);
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-source-card__headline {
|
.tracking-question-source-card__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-question-source-card__header-left {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
width: 100%;
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-question-source-card__site-name {
|
||||||
|
color: #0f172a;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-source-card__indexes {
|
.tracking-question-source-card__indexes {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 4px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2162,15 +2137,22 @@ const contentCitationColumns = computed(() => [
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-width: 34px;
|
min-width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
padding: 0 8px;
|
padding: 0 6px;
|
||||||
border-radius: 999px;
|
border-radius: 6px;
|
||||||
background: #e8f1ff;
|
background: #f1f5f9;
|
||||||
color: #1d4ed8;
|
color: #475569;
|
||||||
font-size: 12px;
|
font-size: 11px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-question-source-card:hover .tracking-question-source-card__index {
|
||||||
|
background: #eff6ff;
|
||||||
|
color: #2563eb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-source-card__index--secondary {
|
.tracking-question-source-card__index--secondary {
|
||||||
@@ -2183,45 +2165,52 @@ const contentCitationColumns = computed(() => [
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-width: 34px;
|
min-width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
padding: 0 8px;
|
padding: 0 6px;
|
||||||
border-radius: 999px;
|
border-radius: 6px;
|
||||||
border: 1px solid #bfdbfe;
|
border: 1px solid #bfdbfe;
|
||||||
background: #eff6ff;
|
background: #eff6ff;
|
||||||
color: #2563eb;
|
color: #2563eb;
|
||||||
font-size: 12px;
|
font-size: 11px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-source-card__headline strong {
|
|
||||||
display: block;
|
|
||||||
color: #2563eb;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tracking-question-source-card__divider {
|
|
||||||
color: #cbd5e1;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tracking-question-source-card__title {
|
.tracking-question-source-card__title {
|
||||||
color: #475569;
|
color: #334155;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
flex: 1;
|
font-weight: 500;
|
||||||
white-space: nowrap;
|
line-height: 1.45;
|
||||||
|
word-break: break-all;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-source-card__badge {
|
.tracking-question-source-card__badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 20px;
|
||||||
|
padding: 0 8px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-left: auto;
|
line-height: 1;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
font-size: 11px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-question-source-card__badge--published {
|
||||||
|
border: 1px solid #a7f3d0;
|
||||||
|
background: #ecfdf5;
|
||||||
|
color: #047857;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-imitation-trigger {
|
.tracking-question-imitation-trigger {
|
||||||
@@ -2231,22 +2220,19 @@ const contentCitationColumns = computed(() => [
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border: 1px solid #dbeafe;
|
border: 1px solid #e2e8f0;
|
||||||
border-radius: 8px;
|
border-radius: 6px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
color: #2563eb;
|
color: #64748b;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(1px);
|
transform: scale(0.95);
|
||||||
transition:
|
transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
opacity 0.18s ease,
|
|
||||||
transform 0.18s ease,
|
|
||||||
border-color 0.18s ease,
|
|
||||||
background-color 0.18s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-imitation-trigger:hover {
|
.tracking-question-imitation-trigger:hover {
|
||||||
border-color: #93c5fd;
|
border-color: #2563eb;
|
||||||
|
color: #2563eb;
|
||||||
background: #eff6ff;
|
background: #eff6ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2259,20 +2245,16 @@ const contentCitationColumns = computed(() => [
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
right: 16px;
|
right: 16px;
|
||||||
|
transform: translateY(-50%);
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
transform: translateY(calc(-50% + 1px));
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-source-card:hover .tracking-question-imitation-trigger,
|
.tracking-question-source-card:hover .tracking-question-imitation-trigger,
|
||||||
.tracking-question-source-card:focus-within .tracking-question-imitation-trigger {
|
.tracking-question-source-card:focus-within .tracking-question-imitation-trigger {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: scale(1);
|
||||||
}
|
|
||||||
|
|
||||||
.tracking-question-source-card:hover .tracking-question-source-card__actions,
|
|
||||||
.tracking-question-source-card:focus-within .tracking-question-source-card__actions {
|
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-imitation-trigger--table-action {
|
.tracking-question-imitation-trigger--table-action {
|
||||||
@@ -2289,10 +2271,12 @@ const contentCitationColumns = computed(() => [
|
|||||||
.tracking-question-source-card__link {
|
.tracking-question-source-card__link {
|
||||||
color: #94a3b8;
|
color: #94a3b8;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
width: 100%;
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-content-card__copy a {
|
.tracking-question-content-card__copy a {
|
||||||
@@ -2345,11 +2329,11 @@ const contentCitationColumns = computed(() => [
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
border-bottom: 1px solid #e2e8f0;
|
border-bottom: 1px solid #e2e8f0;
|
||||||
padding: 16px 24px;
|
padding: 12px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.modern-table .ant-table-tbody > tr > td) {
|
:deep(.modern-table .ant-table-tbody > tr > td) {
|
||||||
padding: 16px 24px;
|
padding: 12px 16px;
|
||||||
border-bottom: 1px solid #f1f5f9;
|
border-bottom: 1px solid #f1f5f9;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
transition: background-color 0.2s ease;
|
transition: background-color 0.2s ease;
|
||||||
@@ -2428,10 +2412,10 @@ const contentCitationColumns = computed(() => [
|
|||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||||
color: #0ea5e9;
|
color: #0ea5e9;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
background: #f0f9ff;
|
background: #f0f9ff;
|
||||||
padding: 4px 10px;
|
padding: 3px 8px;
|
||||||
border-radius: 8px;
|
border-radius: 6px;
|
||||||
border: 1px solid #e0f2fe;
|
border: 1px solid #e0f2fe;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2547,9 +2531,29 @@ const contentCitationColumns = computed(() => [
|
|||||||
color: #b91c1c;
|
color: #b91c1c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1080px) {
|
@media (max-width: 1400px) {
|
||||||
|
.tracking-question-grid {
|
||||||
|
grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
|
||||||
|
grid-auto-rows: calc(100vh - 300px);
|
||||||
|
min-height: 500px;
|
||||||
|
max-height: 650px;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
.tracking-question-grid {
|
.tracking-question-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
grid-auto-rows: auto;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-question-card {
|
||||||
|
height: 550px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-question-card--span-2 {
|
||||||
|
grid-column: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-task-debug-grid,
|
.tracking-task-debug-grid,
|
||||||
@@ -2564,11 +2568,12 @@ const contentCitationColumns = computed(() => [
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-page__copy h1 {
|
.tracking-question-page__copy h1 {
|
||||||
font-size: 26px;
|
font-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-card {
|
.tracking-question-card {
|
||||||
padding: 22px 18px;
|
padding: 22px 18px;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-question-card__header--split,
|
.tracking-question-card__header--split,
|
||||||
|
|||||||
@@ -771,7 +771,7 @@ function openQuestion(question: MonitoringHotQuestion): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openCitedArticle(item: MonitoringCitedArticle): void {
|
function openCitedArticle(item: MonitoringCitedArticle): void {
|
||||||
if (item.source_type === 'manual_mark') {
|
if (item.source_type === 'manual_mark' || item.source_type === 'media_supply_order_item') {
|
||||||
openExternalURL(item.source_url)
|
openExternalURL(item.source_url)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1067,12 +1067,16 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
v-if="formatDelta(card.value, card.previous)"
|
v-if="formatDelta(card.value, card.previous)"
|
||||||
:class="[
|
:class="[
|
||||||
'metric-card__delta',
|
'metric-card__delta',
|
||||||
(card.value ?? 0) >= (card.previous ?? 0)
|
(card.value ?? 0) > (card.previous ?? 0)
|
||||||
? 'metric-card__delta--up'
|
? 'metric-card__delta--up'
|
||||||
: 'metric-card__delta--down',
|
: (card.value ?? 0) < (card.previous ?? 0)
|
||||||
|
? 'metric-card__delta--down'
|
||||||
|
: 'metric-card__delta--flat',
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
{{ formatDelta(card.value, card.previous) }}
|
<span v-if="(card.value ?? 0) > (card.previous ?? 0)" class="trend-arrow">↑</span>
|
||||||
|
<span v-else-if="(card.value ?? 0) < (card.previous ?? 0)" class="trend-arrow">↓</span>
|
||||||
|
{{ (formatDelta(card.value, card.previous) ?? '').replace(/[+-]/, '') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
@@ -1133,10 +1137,6 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
<div class="tracking-panel__header">
|
<div class="tracking-panel__header">
|
||||||
<div>
|
<div>
|
||||||
<h3 class="panel-title">AI 平台状态</h3>
|
<h3 class="panel-title">AI 平台状态</h3>
|
||||||
<span class="tracking-panel__helper">
|
|
||||||
只读展示 desktop-client
|
|
||||||
的绑定、授权和当前采样状态。授权入口放在桌面端,抓取过程静默执行。
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1145,6 +1145,7 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
v-for="platform in aiPlatformStatusCards"
|
v-for="platform in aiPlatformStatusCards"
|
||||||
:key="platform.id"
|
:key="platform.id"
|
||||||
class="ai-platform-status-card"
|
class="ai-platform-status-card"
|
||||||
|
:style="{ '--platform-accent': platform.accent }"
|
||||||
>
|
>
|
||||||
<div class="ai-platform-status-card__head">
|
<div class="ai-platform-status-card__head">
|
||||||
<div class="ai-platform-status-card__identity">
|
<div class="ai-platform-status-card__identity">
|
||||||
@@ -1354,7 +1355,6 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
<div class="tracking-panel__header tracking-panel__header--list">
|
<div class="tracking-panel__header tracking-panel__header--list">
|
||||||
<div class="tracking-panel__header-left">
|
<div class="tracking-panel__header-left">
|
||||||
<h3 class="panel-title">{{ t('tracking.hotQuestionsTitle') }}</h3>
|
<h3 class="panel-title">{{ t('tracking.hotQuestionsTitle') }}</h3>
|
||||||
<span class="tracking-panel__helper">{{ t('tracking.hotQuestionsHint') }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="hotQuestionSearch"
|
v-model:value="hotQuestionSearch"
|
||||||
@@ -1460,6 +1460,8 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
{{
|
{{
|
||||||
item.source_type === 'manual_mark'
|
item.source_type === 'manual_mark'
|
||||||
? t('tracking.manualMarkedArticle')
|
? t('tracking.manualMarkedArticle')
|
||||||
|
: item.source_type === 'media_supply_order_item'
|
||||||
|
? t('tracking.mediaSupplySubmissionCitation')
|
||||||
: item.publish_platform
|
: item.publish_platform
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
@@ -1594,8 +1596,16 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
gap: 14px;
|
gap: 14px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
border: 1px solid #e6edf5;
|
border: 1px solid #e2e8f0;
|
||||||
background: linear-gradient(180deg, #ffffff 0%, #fbfdff 100%);
|
background: #ffffff;
|
||||||
|
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-platform-status-card:hover {
|
||||||
|
border-color: var(--platform-accent) !important;
|
||||||
|
box-shadow: 0 10px 20px -8px var(--platform-accent);
|
||||||
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ai-platform-status-card__head {
|
.ai-platform-status-card__head {
|
||||||
@@ -1613,27 +1623,28 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
|
|
||||||
.ai-platform-status-card__identity strong {
|
.ai-platform-status-card__identity strong {
|
||||||
display: block;
|
display: block;
|
||||||
color: #111827;
|
color: #0f172a;
|
||||||
font-size: 16px;
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ai-platform-status-card__identity p {
|
.ai-platform-status-card__identity p {
|
||||||
margin: 6px 0 0;
|
margin: 4px 0 0;
|
||||||
color: #667085;
|
color: #64748b;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ai-platform-status-card__badge {
|
.ai-platform-status-card__badge {
|
||||||
width: 42px;
|
width: 36px;
|
||||||
height: 42px;
|
height: 36px;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: 14px;
|
border-radius: 10px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
@@ -1649,18 +1660,19 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 10px 12px;
|
padding: 8px 12px;
|
||||||
border-radius: 12px;
|
border-radius: 8px;
|
||||||
background: #f8fafc;
|
background: #f8fafc;
|
||||||
|
border: 1px solid #f1f5f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ai-platform-status-card__row span {
|
.ai-platform-status-card__row span {
|
||||||
color: #667085;
|
color: #64748b;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ai-platform-status-card__row strong {
|
.ai-platform-status-card__row strong {
|
||||||
color: #111827;
|
color: #0f172a;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
@@ -1699,9 +1711,11 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-panel {
|
:deep(.tracking-panel.ant-card) {
|
||||||
border-radius: 12px;
|
border-radius: 16px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
|
border: 1px solid rgba(226, 232, 240, 0.8) !important;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.02), 0 10px 15px -3px rgba(0, 0, 0, 0.03), 0 0 0 1px rgba(0, 0, 0, 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-panel--citation {
|
.tracking-panel--citation {
|
||||||
@@ -1718,6 +1732,8 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
border-bottom: 1px solid #f1f5f9;
|
||||||
|
padding-bottom: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-panel__header-left {
|
.tracking-panel__header-left {
|
||||||
@@ -1731,9 +1747,9 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
|
|
||||||
.panel-title {
|
.panel-title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 17px;
|
font-size: 16px;
|
||||||
font-weight: 700;
|
font-weight: 600;
|
||||||
color: #111827;
|
color: #0f172a;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
@@ -1743,9 +1759,9 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 4px;
|
width: 4px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
background: linear-gradient(180deg, #3b82f6 0%, #1d4ed8 100%);
|
background: #2563eb;
|
||||||
border-radius: 4px;
|
border-radius: 2px;
|
||||||
margin-right: 10px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracking-panel__description {
|
.tracking-panel__description {
|
||||||
@@ -1818,43 +1834,69 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.metric-card {
|
.metric-card {
|
||||||
background: #f7f9fc;
|
background: #ffffff;
|
||||||
border: 1px solid #f0f3fa;
|
border: 1px solid #e2e8f0;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 16px;
|
padding: 20px;
|
||||||
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card:hover {
|
||||||
|
border-color: #3b82f6;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(59, 130, 246, 0.05), 0 4px 6px -2px rgba(59, 130, 246, 0.02);
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric-card__label {
|
.metric-card__label {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: #8c8c8c;
|
color: #64748b;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric-card__value-row {
|
.metric-card__value-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
margin-top: 10px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric-card__value-row strong {
|
.metric-card__value-row strong {
|
||||||
font-size: 24px;
|
font-size: 28px;
|
||||||
font-weight: 800;
|
font-weight: 700;
|
||||||
color: #141414;
|
color: #0f172a;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric-card__delta {
|
.metric-card__delta {
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
font-weight: 700;
|
font-weight: 600;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 6px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric-card__delta--up {
|
.metric-card__delta--up {
|
||||||
color: #15803d;
|
color: #16a34a;
|
||||||
|
background: #f0fdf4;
|
||||||
|
border: 1px solid #bbf7d0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric-card__delta--down {
|
.metric-card__delta--down {
|
||||||
color: #dc2626;
|
color: #dc2626;
|
||||||
|
background: #fef2f2;
|
||||||
|
border: 1px solid #fecaca;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card__delta--flat {
|
||||||
|
color: #475569;
|
||||||
|
background: #f1f5f9;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.platform-table,
|
.platform-table,
|
||||||
@@ -1876,19 +1918,27 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
|
|
||||||
.platform-table__head,
|
.platform-table__head,
|
||||||
.citation-table__head {
|
.citation-table__head {
|
||||||
background: #fafafa;
|
background: #f8fafc;
|
||||||
color: #8c8c8c;
|
color: #64748b;
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
padding: 12px 16px;
|
padding: 10px 16px;
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid #e2e8f0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.platform-table__row,
|
.platform-table__row,
|
||||||
.citation-table__row {
|
.citation-table__row {
|
||||||
padding: 16px;
|
padding: 12px 16px;
|
||||||
border-bottom: 1px solid #f9f9f9;
|
border-bottom: 1px solid #f1f5f9;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.platform-table__row:hover,
|
||||||
|
.citation-table__row:hover {
|
||||||
|
background: #f8fafc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.platform-table__row:last-child,
|
.platform-table__row:last-child,
|
||||||
@@ -2043,7 +2093,7 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
border: 1px solid #f0f3fa;
|
border: 1px solid #f0f3fa;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
overflow: hidden;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.citation-ranking-sheet__head,
|
.citation-ranking-sheet__head,
|
||||||
@@ -2475,19 +2525,51 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1080px) {
|
@media (max-width: 1400px) {
|
||||||
|
.citation-ranking-layout {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.citation-ranking-visual {
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 32px;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.citation-ranking-legend {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
.tracking-grid--top,
|
.tracking-grid--top,
|
||||||
.tracking-grid--middle,
|
.tracking-grid--middle {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.citation-ranking-visual {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.citation-ranking-legend {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1080px) {
|
||||||
.tracking-grid--bottom,
|
.tracking-grid--bottom,
|
||||||
.detail-grid,
|
.detail-grid,
|
||||||
.detail-grid--bottom {
|
.detail-grid--bottom {
|
||||||
grid-template-columns: minmax(0, 1fr);
|
grid-template-columns: minmax(0, 1fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
.citation-ranking-layout {
|
|
||||||
grid-template-columns: minmax(0, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
.citation-ranking-visual {
|
.citation-ranking-visual {
|
||||||
min-height: auto;
|
min-height: auto;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1,17 +0,0 @@
|
|||||||
# 跨境电商专项会议核心汇报
|
|
||||||
本次会议完成团队标准化流程梳理、主流平台实操复盘及行业专家合作模式洽谈,核心成果与待决策事项如下:
|
|
||||||
|
|
||||||
## 一、团队体系梳理成果
|
|
||||||
1. **搭建3岗业务闭环**:明确厂家对接、平台对接、双向统筹三大核心岗位,覆盖供应链到平台运营全链路,打通两端信息壁垒,保障业务高效协同。
|
|
||||||
2. **划定四大业务板块**:锁定前端运营、客服服务、资金核算、货品对接核心模块,全面覆盖店铺日常运营场景。
|
|
||||||
|
|
||||||
## 二、平台实操经验沉淀
|
|
||||||
重点复盘Temu、希音两大跨境平台上货全流程,梳理形成平台上架规则、产品适配标准及流程优化方向,为后续精细化运营提供落地支撑。
|
|
||||||
|
|
||||||
## 三、专家合作方案(待决策)
|
|
||||||
已与行业资深专家初步沟通两类合作模式,**报价均有协商空间,洽谈可兼顾性价比与合作诚意**:
|
|
||||||
- **全职顾问模式**:月薪报价20000元,每周在岗2-3天,提供全流程落地指导、实操答疑、运营优化帮扶
|
|
||||||
- **轻量咨询模式**:包月报价5000元,线上按需响应,提供业务流程梳理、运营方案输出、团队标准化搭建指导
|
|
||||||
|
|
||||||
## 后续建议
|
|
||||||
可结合团队当前运营阶段与预算选定适配模式:若需快速跑通业务、手把手带教团队,可优先推进全职顾问模式洽谈;若仅需方向把控与阶段性问题指导,可选择轻量咨询模式,加速团队标准化落地与业务起量。
|
|
||||||
@@ -46,7 +46,9 @@ func main() {
|
|||||||
if mediaSupplyService == nil {
|
if mediaSupplyService == nil {
|
||||||
mediaSupplyService = tenantapp.NewMediaSupplyService(app.DB, app.Redis, app.Logger, app.ConfigStore).WithObjectStorage(app.ObjectStorage)
|
mediaSupplyService = tenantapp.NewMediaSupplyService(app.DB, app.Redis, app.Logger, app.ConfigStore).WithObjectStorage(app.ObjectStorage)
|
||||||
}
|
}
|
||||||
tenantapp.NewMediaSupplyWorker(mediaSupplyService, app.DB, app.Logger).Start(workerCtx)
|
tenantapp.NewMediaSupplyWorker(mediaSupplyService, app.DB, app.Logger).
|
||||||
|
WithMonitoringPool(app.MonitoringDB).
|
||||||
|
Start(workerCtx)
|
||||||
knowledgeCleanupService := tenantapp.NewKnowledgeService(
|
knowledgeCleanupService := tenantapp.NewKnowledgeService(
|
||||||
app.DB,
|
app.DB,
|
||||||
app.RetrievalProvider,
|
app.RetrievalProvider,
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type MediaSupplyWorker struct {
|
type MediaSupplyWorker struct {
|
||||||
service *MediaSupplyService
|
service *MediaSupplyService
|
||||||
pool *pgxpool.Pool
|
pool *pgxpool.Pool
|
||||||
logger *zap.Logger
|
monitoringPool *pgxpool.Pool
|
||||||
|
logger *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type mediaSupplySyncJob struct {
|
type mediaSupplySyncJob struct {
|
||||||
@@ -59,6 +60,13 @@ func NewMediaSupplyWorker(service *MediaSupplyService, pool *pgxpool.Pool, logge
|
|||||||
return &MediaSupplyWorker{service: service, pool: pool, logger: logger}
|
return &MediaSupplyWorker{service: service, pool: pool, logger: logger}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *MediaSupplyWorker) WithMonitoringPool(monitoringPool *pgxpool.Pool) *MediaSupplyWorker {
|
||||||
|
if w != nil {
|
||||||
|
w.monitoringPool = monitoringPool
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
func (w *MediaSupplyWorker) Start(ctx context.Context) {
|
func (w *MediaSupplyWorker) Start(ctx context.Context) {
|
||||||
if w == nil || w.service == nil || w.pool == nil {
|
if w == nil || w.service == nil || w.pool == nil {
|
||||||
return
|
return
|
||||||
@@ -917,7 +925,165 @@ func (w *MediaSupplyWorker) updateOrderBacklinks(ctx context.Context, order medi
|
|||||||
`, order.ID, "published", remaining, firstMatchedMeijiequanOrderCode(matches), firstMatchedMeijiequanOrderID(matches)); err != nil {
|
`, order.ID, "published", remaining, firstMatchedMeijiequanOrderCode(matches), firstMatchedMeijiequanOrderID(matches)); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
return updatedLinks, tx.Commit(ctx)
|
if err := tx.Commit(ctx); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
w.refreshMonitoringAttributionForMediaSupplyBacklinks(ctx, order.ID)
|
||||||
|
return updatedLinks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *MediaSupplyWorker) refreshMonitoringAttributionForMediaSupplyBacklinks(ctx context.Context, orderID int64) {
|
||||||
|
if w == nil || w.monitoringPool == nil || w.pool == nil || orderID <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := w.pool.Query(ctx, `
|
||||||
|
SELECT
|
||||||
|
o.tenant_id,
|
||||||
|
i.id,
|
||||||
|
i.external_article_url
|
||||||
|
FROM media_supply_order_items i
|
||||||
|
JOIN media_supply_orders o ON o.id = i.order_id
|
||||||
|
WHERE o.id = $1
|
||||||
|
AND o.deleted_at IS NULL
|
||||||
|
AND NULLIF(TRIM(COALESCE(i.external_article_url, '')), '') IS NOT NULL
|
||||||
|
`, orderID)
|
||||||
|
if err != nil {
|
||||||
|
w.logWarn("media supply monitoring attribution backlink lookup failed", zap.Int64("order_id", orderID), zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
type backlinkSource struct {
|
||||||
|
tenantID int64
|
||||||
|
itemID int64
|
||||||
|
url string
|
||||||
|
}
|
||||||
|
sources := make([]backlinkSource, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
var source backlinkSource
|
||||||
|
if scanErr := rows.Scan(&source.tenantID, &source.itemID, &source.url); scanErr != nil {
|
||||||
|
w.logWarn("media supply monitoring attribution backlink scan failed", zap.Int64("order_id", orderID), zap.Error(scanErr))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
source.url = strings.TrimSpace(source.url)
|
||||||
|
if source.tenantID <= 0 || source.itemID <= 0 || source.url == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sources = append(sources, source)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
w.logWarn("media supply monitoring attribution backlink iterate failed", zap.Int64("order_id", orderID), zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, source := range sources {
|
||||||
|
if _, err := w.refreshMonitoringAttributionForMediaSupplyBacklink(ctx, source.tenantID, source.itemID, source.url); err != nil {
|
||||||
|
w.logWarn(
|
||||||
|
"media supply monitoring attribution refresh failed",
|
||||||
|
zap.Int64("tenant_id", source.tenantID),
|
||||||
|
zap.Int64("order_id", orderID),
|
||||||
|
zap.Int64("item_id", source.itemID),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *MediaSupplyWorker) refreshMonitoringAttributionForMediaSupplyBacklink(ctx context.Context, tenantID, itemID int64, rawURL string) (int64, error) {
|
||||||
|
matchKeys := monitoringCitationCandidateKeys(rawURL, normalizeCitationURL(rawURL))
|
||||||
|
if len(matchKeys) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizedURL := normalizeCitationURL(rawURL)
|
||||||
|
host, registrableDomain, _, _ := deriveCitationURLParts(rawURL, normalizedURL)
|
||||||
|
candidateURLs := uniqueTrimmedStrings(rawURL, normalizedURL)
|
||||||
|
rows, err := w.monitoringPool.Query(ctx, `
|
||||||
|
SELECT id, cited_url, normalized_url
|
||||||
|
FROM monitoring_citation_facts
|
||||||
|
WHERE tenant_id = $1
|
||||||
|
AND (
|
||||||
|
(content_source_type IS NULL AND content_source_id IS NULL)
|
||||||
|
OR content_source_type = ANY($5::text[])
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
cited_url = ANY($2::text[])
|
||||||
|
OR normalized_url = ANY($2::text[])
|
||||||
|
OR NULLIF(LOWER(TRIM(host)), '') = NULLIF($3, '')
|
||||||
|
OR NULLIF(LOWER(TRIM(registrable_domain)), '') = NULLIF($4, '')
|
||||||
|
OR NULLIF(LOWER(TRIM(site_key)), '') = NULLIF($4, '')
|
||||||
|
)
|
||||||
|
`, tenantID, candidateURLs, strings.ToLower(strings.TrimSpace(host)), strings.ToLower(strings.TrimSpace(registrableDomain)), []string{
|
||||||
|
monitoringContentSourcePublishedArticle,
|
||||||
|
monitoringContentSourceManualMark,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
matchKeySet := make(map[string]struct{}, len(matchKeys))
|
||||||
|
for _, key := range matchKeys {
|
||||||
|
matchKeySet[strings.TrimSpace(key)] = struct{}{}
|
||||||
|
}
|
||||||
|
matchedIDs := make([]int64, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
var id int64
|
||||||
|
var citedURL string
|
||||||
|
var factNormalizedURL string
|
||||||
|
if scanErr := rows.Scan(&id, &citedURL, &factNormalizedURL); scanErr != nil {
|
||||||
|
return 0, scanErr
|
||||||
|
}
|
||||||
|
for _, factKey := range monitoringCitationCandidateKeys(citedURL, factNormalizedURL) {
|
||||||
|
if _, ok := matchKeySet[strings.TrimSpace(factKey)]; ok {
|
||||||
|
matchedIDs = append(matchedIDs, id)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if len(matchedIDs) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tag, err := w.monitoringPool.Exec(ctx, `
|
||||||
|
UPDATE monitoring_citation_facts
|
||||||
|
SET content_source_type = $3,
|
||||||
|
content_source_id = $2
|
||||||
|
WHERE tenant_id = $1
|
||||||
|
AND id = ANY($4::bigint[])
|
||||||
|
AND (
|
||||||
|
(content_source_type IS NULL AND content_source_id IS NULL)
|
||||||
|
OR content_source_type = ANY($5::text[])
|
||||||
|
)
|
||||||
|
`, tenantID, itemID, monitoringContentSourceMediaSupplyOrderItem, matchedIDs, []string{
|
||||||
|
monitoringContentSourcePublishedArticle,
|
||||||
|
monitoringContentSourceManualMark,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return tag.RowsAffected(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func uniqueTrimmedStrings(values ...string) []string {
|
||||||
|
result := make([]string, 0, len(values))
|
||||||
|
seen := make(map[string]struct{}, len(values))
|
||||||
|
for _, value := range values {
|
||||||
|
trimmed := strings.TrimSpace(value)
|
||||||
|
if trimmed == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, exists := seen[trimmed]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[trimmed] = struct{}{}
|
||||||
|
result = append(result, trimmed)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *MediaSupplyWorker) claimSyncJob(ctx context.Context) (*mediaSupplySyncJob, bool, error) {
|
func (w *MediaSupplyWorker) claimSyncJob(ctx context.Context) (*mediaSupplySyncJob, bool, error) {
|
||||||
|
|||||||
@@ -2819,10 +2819,10 @@ func (s *MonitoringCallbackService) loadAliasResolutions(ctx context.Context, tx
|
|||||||
normalized_url,
|
normalized_url,
|
||||||
article_id,
|
article_id,
|
||||||
publish_record_id,
|
publish_record_id,
|
||||||
'published_article' AS source_type,
|
'published_article' AS source_type,
|
||||||
article_id AS source_id,
|
article_id AS source_id,
|
||||||
1 AS source_priority,
|
2 AS source_priority,
|
||||||
registrable_domain,
|
registrable_domain,
|
||||||
site_key,
|
site_key,
|
||||||
host,
|
host,
|
||||||
last_path_segment,
|
last_path_segment,
|
||||||
@@ -2837,10 +2837,10 @@ func (s *MonitoringCallbackService) loadAliasResolutions(ctx context.Context, tx
|
|||||||
normalized_url,
|
normalized_url,
|
||||||
NULL AS article_id,
|
NULL AS article_id,
|
||||||
NULL AS publish_record_id,
|
NULL AS publish_record_id,
|
||||||
'manual_mark' AS source_type,
|
'manual_mark' AS source_type,
|
||||||
id AS source_id,
|
id AS source_id,
|
||||||
2 AS source_priority,
|
3 AS source_priority,
|
||||||
registrable_domain,
|
registrable_domain,
|
||||||
site_key,
|
site_key,
|
||||||
host,
|
host,
|
||||||
last_path_segment,
|
last_path_segment,
|
||||||
@@ -2855,10 +2855,10 @@ func (s *MonitoringCallbackService) loadAliasResolutions(ctx context.Context, tx
|
|||||||
normalized_url,
|
normalized_url,
|
||||||
article_id,
|
article_id,
|
||||||
publish_record_id,
|
publish_record_id,
|
||||||
source_type,
|
source_type,
|
||||||
source_id,
|
source_id,
|
||||||
source_priority,
|
source_priority,
|
||||||
registrable_domain,
|
registrable_domain,
|
||||||
site_key,
|
site_key,
|
||||||
host,
|
host,
|
||||||
last_path_segment,
|
last_path_segment,
|
||||||
@@ -2916,6 +2916,36 @@ func (s *MonitoringCallbackService) loadAliasResolutions(ctx context.Context, tx
|
|||||||
return nil, response.ErrInternal(50041, "alias_scan_failed", "failed to iterate article aliases")
|
return nil, response.ErrInternal(50041, "alias_scan_failed", "failed to iterate article aliases")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mediaAliases, err := loadMonitoringMediaSupplyPublishedAliases(ctx, s.businessPool, tenantID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, alias := range mediaAliases {
|
||||||
|
candidate := monitoringAliasCandidate{
|
||||||
|
NormalizedURL: alias.NormalizedURL,
|
||||||
|
SourceType: strings.TrimSpace(alias.SourceType),
|
||||||
|
SourcePriority: alias.SourcePriority,
|
||||||
|
TitleKey: normalizeMonitoringAliasTitle(alias.ArticleTitle),
|
||||||
|
MatchKeys: monitoringCitationCandidateKeys(alias.OriginalURL, alias.NormalizedURL),
|
||||||
|
}
|
||||||
|
if alias.ArticleID > 0 {
|
||||||
|
value := alias.ArticleID
|
||||||
|
candidate.ArticleID = &value
|
||||||
|
}
|
||||||
|
if alias.SourceID > 0 {
|
||||||
|
value := alias.SourceID
|
||||||
|
candidate.SourceID = &value
|
||||||
|
}
|
||||||
|
if lastPathSegment := monitoringAliasLastPathSegment(alias.NormalizedURL); lastPathSegment != nil {
|
||||||
|
candidate.LastPathSegment = strings.TrimSpace(*lastPathSegment)
|
||||||
|
}
|
||||||
|
host, registrableDomain, _, _ := deriveCitationURLParts(alias.OriginalURL, alias.NormalizedURL)
|
||||||
|
candidate.Host = strings.ToLower(strings.TrimSpace(host))
|
||||||
|
candidate.RegistrableDomain = strings.ToLower(strings.TrimSpace(registrableDomain))
|
||||||
|
candidate.SiteKey = candidate.RegistrableDomain
|
||||||
|
candidates = append(candidates, candidate)
|
||||||
|
}
|
||||||
|
|
||||||
for _, input := range lookupInputs {
|
for _, input := range lookupInputs {
|
||||||
bestScore := -1
|
bestScore := -1
|
||||||
bestAmbiguous := false
|
bestAmbiguous := false
|
||||||
|
|||||||
@@ -583,6 +583,38 @@ func TestBuildMonitoringCitationFactResolvesContentForQwen(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildMonitoringCitationFactResolvesMediaSupplyContentSource(t *testing.T) {
|
||||||
|
aliasArticleID := int64(42)
|
||||||
|
sourceID := int64(9001)
|
||||||
|
normalizedURL := "https://example.com/submitted/article"
|
||||||
|
input := MonitoringSourceItem{
|
||||||
|
URL: "https://example.com/submitted/article?utm_source=chat",
|
||||||
|
NormalizedURL: &normalizedURL,
|
||||||
|
}
|
||||||
|
aliasMap := map[string]monitoringAliasResolution{
|
||||||
|
normalizedURL: {
|
||||||
|
ArticleID: &aliasArticleID,
|
||||||
|
NormalizedURLKey: normalizedURL,
|
||||||
|
SourceType: monitoringContentSourceMediaSupplyOrderItem,
|
||||||
|
SourceID: &sourceID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fact, ok := buildMonitoringCitationFact(input, aliasMap, true)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("buildMonitoringCitationFact() returned !ok")
|
||||||
|
}
|
||||||
|
if fact.ArticleID == nil || *fact.ArticleID != aliasArticleID {
|
||||||
|
t.Fatalf("ArticleID = %v, want %d", fact.ArticleID, aliasArticleID)
|
||||||
|
}
|
||||||
|
if fact.ContentSourceType == nil || *fact.ContentSourceType != monitoringContentSourceMediaSupplyOrderItem {
|
||||||
|
t.Fatalf("ContentSourceType = %v, want %q", fact.ContentSourceType, monitoringContentSourceMediaSupplyOrderItem)
|
||||||
|
}
|
||||||
|
if fact.ContentSourceID == nil || *fact.ContentSourceID != sourceID {
|
||||||
|
t.Fatalf("ContentSourceID = %v, want %d", fact.ContentSourceID, sourceID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestScoreMonitoringAliasCandidateUsesCanonicalArticleKey(t *testing.T) {
|
func TestScoreMonitoringAliasCandidateUsesCanonicalArticleKey(t *testing.T) {
|
||||||
input := monitoringAliasLookupInput{
|
input := monitoringAliasLookupInput{
|
||||||
NormalizedURL: "https://m.163.com/dy/article/KRPRE8DJ0556MCR0.html",
|
NormalizedURL: "https://m.163.com/dy/article/KRPRE8DJ0556MCR0.html",
|
||||||
@@ -613,6 +645,70 @@ func TestScoreMonitoringAliasCandidateRejectsSameDomainDifferentArticle(t *testi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMonitoringPublishedAliasIndexMatchesMediaSupplySubmission(t *testing.T) {
|
||||||
|
index := make(monitoringPublishedAliasIndex)
|
||||||
|
sourceID := int64(9001)
|
||||||
|
mediaAlias := monitoringPublishedAlias{
|
||||||
|
ArticleID: 42,
|
||||||
|
ArticleTitle: "投稿文章",
|
||||||
|
PublishPlatform: monitoringMediaSupplySubmissionCitationLabel,
|
||||||
|
OriginalURL: "https://m.example.com/submitted/article?id=123&utm_source=chat",
|
||||||
|
NormalizedURL: "https://www.example.com/submitted/article?id=123",
|
||||||
|
SourceType: monitoringContentSourceMediaSupplyOrderItem,
|
||||||
|
SourceID: sourceID,
|
||||||
|
SourcePriority: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
addMonitoringPublishedAliasToIndex(index, mediaAlias)
|
||||||
|
|
||||||
|
match, ok := index.Match(
|
||||||
|
"https://www.example.com/submitted/article?id=123&utm_medium=ai",
|
||||||
|
"https://www.example.com/submitted/article?id=123",
|
||||||
|
)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Match() = false, want media supply alias match")
|
||||||
|
}
|
||||||
|
if match.SourceType != monitoringContentSourceMediaSupplyOrderItem {
|
||||||
|
t.Fatalf("SourceType = %q, want %q", match.SourceType, monitoringContentSourceMediaSupplyOrderItem)
|
||||||
|
}
|
||||||
|
if match.SourceID != sourceID {
|
||||||
|
t.Fatalf("SourceID = %d, want %d", match.SourceID, sourceID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonitoringPublishedAliasIndexPrefersMediaSupplySubmission(t *testing.T) {
|
||||||
|
index := make(monitoringPublishedAliasIndex)
|
||||||
|
normalizedURL := "https://www.example.com/submitted/article?id=123"
|
||||||
|
|
||||||
|
addMonitoringPublishedAliasToIndex(index, monitoringPublishedAlias{
|
||||||
|
ArticleID: 42,
|
||||||
|
OriginalURL: normalizedURL,
|
||||||
|
NormalizedURL: normalizedURL,
|
||||||
|
SourceType: monitoringContentSourcePublishedArticle,
|
||||||
|
SourceID: 42,
|
||||||
|
SourcePriority: 2,
|
||||||
|
})
|
||||||
|
addMonitoringPublishedAliasToIndex(index, monitoringPublishedAlias{
|
||||||
|
ArticleID: 42,
|
||||||
|
OriginalURL: normalizedURL,
|
||||||
|
NormalizedURL: normalizedURL,
|
||||||
|
SourceType: monitoringContentSourceMediaSupplyOrderItem,
|
||||||
|
SourceID: 9001,
|
||||||
|
SourcePriority: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
match, ok := index.Match(normalizedURL, normalizedURL)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Match() = false, want media supply alias match")
|
||||||
|
}
|
||||||
|
if match.SourceType != monitoringContentSourceMediaSupplyOrderItem {
|
||||||
|
t.Fatalf("SourceType = %q, want %q", match.SourceType, monitoringContentSourceMediaSupplyOrderItem)
|
||||||
|
}
|
||||||
|
if match.SourceID != 9001 {
|
||||||
|
t.Fatalf("SourceID = %d, want 9001", match.SourceID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuildMonitoringRawPayloadDeepSeekIncludesSearchResultsInCitationInputs(t *testing.T) {
|
func TestBuildMonitoringRawPayloadDeepSeekIncludesSearchResultsInCitationInputs(t *testing.T) {
|
||||||
searchTitle := "搜索网页结果"
|
searchTitle := "搜索网页结果"
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,78 @@ type monitoringAliasQuerier interface {
|
|||||||
Query(ctx context.Context, sql string, args ...interface{}) (pgx.Rows, error)
|
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, `
|
rows, err := q.Query(ctx, `
|
||||||
WITH alias_sources AS (
|
WITH alias_sources AS (
|
||||||
SELECT
|
SELECT
|
||||||
@@ -42,11 +113,11 @@ func loadMonitoringPublishedAliasIndex(ctx context.Context, q monitoringAliasQue
|
|||||||
COALESCE(publish_platform_name_snapshot, '已发布内容') AS publish_platform,
|
COALESCE(publish_platform_name_snapshot, '已发布内容') AS publish_platform,
|
||||||
original_url,
|
original_url,
|
||||||
normalized_url,
|
normalized_url,
|
||||||
'published_article' AS source_type,
|
'published_article' AS source_type,
|
||||||
article_id AS source_id,
|
article_id AS source_id,
|
||||||
1 AS source_priority,
|
2 AS source_priority,
|
||||||
updated_at AS sort_updated_at,
|
updated_at AS sort_updated_at,
|
||||||
id AS sort_id
|
id AS sort_id
|
||||||
FROM monitoring_article_url_aliases
|
FROM monitoring_article_url_aliases
|
||||||
WHERE tenant_id = $1
|
WHERE tenant_id = $1
|
||||||
AND confidence = 'high'
|
AND confidence = 'high'
|
||||||
@@ -58,11 +129,11 @@ func loadMonitoringPublishedAliasIndex(ctx context.Context, q monitoringAliasQue
|
|||||||
COALESCE(NULLIF(publish_platform, ''), '外部文章') AS publish_platform,
|
COALESCE(NULLIF(publish_platform, ''), '外部文章') AS publish_platform,
|
||||||
original_url,
|
original_url,
|
||||||
normalized_url,
|
normalized_url,
|
||||||
'manual_mark' AS source_type,
|
'manual_mark' AS source_type,
|
||||||
id AS source_id,
|
id AS source_id,
|
||||||
2 AS source_priority,
|
3 AS source_priority,
|
||||||
updated_at AS sort_updated_at,
|
updated_at AS sort_updated_at,
|
||||||
id AS sort_id
|
id AS sort_id
|
||||||
FROM monitoring_user_marked_articles
|
FROM monitoring_user_marked_articles
|
||||||
WHERE tenant_id = $1
|
WHERE tenant_id = $1
|
||||||
AND expires_at > NOW()
|
AND expires_at > NOW()
|
||||||
@@ -74,9 +145,9 @@ func loadMonitoringPublishedAliasIndex(ctx context.Context, q monitoringAliasQue
|
|||||||
publish_platform,
|
publish_platform,
|
||||||
original_url,
|
original_url,
|
||||||
normalized_url,
|
normalized_url,
|
||||||
source_type,
|
source_type,
|
||||||
source_id,
|
source_id,
|
||||||
source_priority
|
source_priority
|
||||||
FROM alias_sources
|
FROM alias_sources
|
||||||
ORDER BY source_priority ASC, sort_updated_at DESC, sort_id DESC
|
ORDER BY source_priority ASC, sort_updated_at DESC, sort_id DESC
|
||||||
`, tenantID)
|
`, tenantID)
|
||||||
@@ -111,32 +182,44 @@ func loadMonitoringPublishedAliasIndex(ctx context.Context, q monitoringAliasQue
|
|||||||
item.PublishRecordID = nullableInt64Value(publishRecordID)
|
item.PublishRecordID = nullableInt64Value(publishRecordID)
|
||||||
item.OriginalURL = originalURL
|
item.OriginalURL = originalURL
|
||||||
item.NormalizedURL = normalizedURL
|
item.NormalizedURL = normalizedURL
|
||||||
for _, key := range monitoringCitationCandidateKeys(originalURL, normalizedURL) {
|
addMonitoringPublishedAliasToIndex(index, item)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if err := rows.Err(); err != nil {
|
if err := rows.Err(); err != nil {
|
||||||
return nil, responseInternalAliasScanError()
|
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
|
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) {
|
func (index monitoringPublishedAliasIndex) Match(rawURL, normalizedURL string) (monitoringPublishedAlias, bool) {
|
||||||
for _, key := range monitoringCitationCandidateKeys(rawURL, normalizedURL) {
|
for _, key := range monitoringCitationCandidateKeys(rawURL, normalizedURL) {
|
||||||
item, ok := index[key]
|
item, ok := index[key]
|
||||||
|
|||||||
@@ -3733,9 +3733,9 @@ func (s *MonitoringService) loadCitedArticleSourceMeta(ctx context.Context, tena
|
|||||||
manualIDs := make([]int64, 0, len(sources))
|
manualIDs := make([]int64, 0, len(sources))
|
||||||
for _, source := range sources {
|
for _, source := range sources {
|
||||||
switch source.SourceType {
|
switch source.SourceType {
|
||||||
case "published_article":
|
case monitoringContentSourcePublishedArticle:
|
||||||
publishedIDs = append(publishedIDs, source.SourceID)
|
publishedIDs = append(publishedIDs, source.SourceID)
|
||||||
case "manual_mark":
|
case monitoringContentSourceManualMark:
|
||||||
manualIDs = append(manualIDs, source.SourceID)
|
manualIDs = append(manualIDs, source.SourceID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3766,7 +3766,7 @@ func (s *MonitoringService) loadCitedArticleSourceMeta(ctx context.Context, tena
|
|||||||
item.ArticleID = &value
|
item.ArticleID = &value
|
||||||
item.ArticleTitle = repairMonitoringMojibake(item.ArticleTitle)
|
item.ArticleTitle = repairMonitoringMojibake(item.ArticleTitle)
|
||||||
item.PublishPlatform = repairMonitoringMojibake(item.PublishPlatform)
|
item.PublishPlatform = repairMonitoringMojibake(item.PublishPlatform)
|
||||||
item.SourceType = "published_article"
|
item.SourceType = monitoringContentSourcePublishedArticle
|
||||||
item.SourceID = articleID
|
item.SourceID = articleID
|
||||||
result[monitoringContentSourceKey(item.SourceType, item.SourceID)] = item
|
result[monitoringContentSourceKey(item.SourceType, item.SourceID)] = item
|
||||||
}
|
}
|
||||||
@@ -3796,13 +3796,68 @@ func (s *MonitoringService) loadCitedArticleSourceMeta(ctx context.Context, tena
|
|||||||
}
|
}
|
||||||
item.ArticleTitle = repairMonitoringMojibake(item.ArticleTitle)
|
item.ArticleTitle = repairMonitoringMojibake(item.ArticleTitle)
|
||||||
item.PublishPlatform = repairMonitoringMojibake(item.PublishPlatform)
|
item.PublishPlatform = repairMonitoringMojibake(item.PublishPlatform)
|
||||||
item.SourceType = "manual_mark"
|
item.SourceType = monitoringContentSourceManualMark
|
||||||
result[monitoringContentSourceKey(item.SourceType, item.SourceID)] = item
|
result[monitoringContentSourceKey(item.SourceType, item.SourceID)] = item
|
||||||
}
|
}
|
||||||
if err := rows.Err(); err != nil {
|
if err := rows.Err(); err != nil {
|
||||||
return nil, response.ErrInternal(50041, "scan_failed", "failed to iterate marked article metadata")
|
return nil, response.ErrInternal(50041, "scan_failed", "failed to iterate marked article metadata")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mediaItems, err := s.loadMediaSupplyCitedArticleSourceMeta(ctx, tenantID, sources)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for key, item := range mediaItems {
|
||||||
|
result[key] = item
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MonitoringService) loadMediaSupplyCitedArticleSourceMeta(ctx context.Context, tenantID int64, sources []monitoringCitedArticleSourceRow) (map[string]MonitoringCitedArticle, error) {
|
||||||
|
result := make(map[string]MonitoringCitedArticle)
|
||||||
|
mediaItemIDs := make([]int64, 0, len(sources))
|
||||||
|
for _, source := range sources {
|
||||||
|
if source.SourceType == monitoringContentSourceMediaSupplyOrderItem {
|
||||||
|
mediaItemIDs = append(mediaItemIDs, source.SourceID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(mediaItemIDs) == 0 {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := s.businessPool.Query(ctx, `
|
||||||
|
SELECT
|
||||||
|
i.id,
|
||||||
|
o.article_id,
|
||||||
|
COALESCE(NULLIF(o.title, ''), NULLIF(i.resource_name_snapshot, ''), '未命名文章') AS article_title,
|
||||||
|
COALESCE(NULLIF(i.resource_name_snapshot, ''), $3) 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 i.id = ANY($2)
|
||||||
|
AND o.deleted_at IS NULL
|
||||||
|
`, tenantID, mediaItemIDs, monitoringMediaSupplySubmissionCitationLabel)
|
||||||
|
if err != nil {
|
||||||
|
return nil, response.ErrInternal(50041, "query_failed", "failed to load media supply citation metadata")
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var item MonitoringCitedArticle
|
||||||
|
var articleID sql.NullInt64
|
||||||
|
if scanErr := rows.Scan(&item.SourceID, &articleID, &item.ArticleTitle, &item.PublishPlatform, &item.SourceURL); scanErr != nil {
|
||||||
|
return nil, response.ErrInternal(50041, "scan_failed", "failed to parse media supply citation metadata")
|
||||||
|
}
|
||||||
|
item.ArticleID = nullableInt64Value(articleID)
|
||||||
|
item.ArticleTitle = repairMonitoringMojibake(item.ArticleTitle)
|
||||||
|
item.PublishPlatform = repairMonitoringMojibake(item.PublishPlatform)
|
||||||
|
item.SourceType = monitoringContentSourceMediaSupplyOrderItem
|
||||||
|
result[monitoringContentSourceKey(item.SourceType, item.SourceID)] = item
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, response.ErrInternal(50041, "scan_failed", "failed to iterate media supply citation metadata")
|
||||||
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4012,7 +4067,7 @@ func (s *MonitoringService) loadCitationsForRuns(ctx context.Context, tenantID i
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
aliasIndex, err := loadMonitoringPublishedAliasIndex(ctx, s.monitoringPool, tenantID)
|
aliasIndex, err := loadMonitoringPublishedAliasIndex(ctx, s.monitoringPool, s.businessPool, tenantID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -4120,7 +4175,7 @@ func (s *MonitoringService) loadCitationsForRuns(ctx context.Context, tenantID i
|
|||||||
articleTitle = &title
|
articleTitle = &title
|
||||||
aliasSourceType := strings.TrimSpace(alias.SourceType)
|
aliasSourceType := strings.TrimSpace(alias.SourceType)
|
||||||
if aliasSourceType == "" {
|
if aliasSourceType == "" {
|
||||||
aliasSourceType = "published_article"
|
aliasSourceType = monitoringContentSourcePublishedArticle
|
||||||
}
|
}
|
||||||
sourceType = &aliasSourceType
|
sourceType = &aliasSourceType
|
||||||
aliasSourceID := alias.SourceID
|
aliasSourceID := alias.SourceID
|
||||||
|
|||||||
Reference in New Issue
Block a user