專案

一般

配置概況

工作單 #199 » P9804001-Single Card High-Frequency Transaction Analysis.json

marlboro chu, 2025-06-30 02:04

 
{
"result" : {
"id" : 636,
"orgId" : 1,
"folderId" : 136,
"folderUid" : "bemeqa2prz7k0d",
"uid" : "feq3het8ilukge",
"name" : "P9804001-Single Card High-Frequency Transaction Analysis",
"kind" : 1,
"type" : "marcusolsson-dynamictext-panel",
"description" : "",
"model" : {
"datasource" : {
"type" : "yesoreyeram-infinity-datasource",
"uid" : "aegaeyjq187b4e"
},
"description" : "",
"fieldConfig" : {
"defaults" : {
"decimals" : 0,
"thresholds" : {
"mode" : "absolute",
"steps" : [ {
"color" : "green",
"value" : null
}, {
"color" : "red",
"value" : 80
} ]
},
"unit" : "locale"
},
"overrides" : [ ]
},
"gridPos" : {
"h" : 9,
"w" : 24,
"x" : 0,
"y" : 71
},
"id" : 9804001,
"options" : {
"afterRender" : "let currentHighTxSortField = 'txCount';\r\nlet currentHighTxOrder = 'desc';\r\n\r\n\r\nconst highTxHeaders = [\r\n { text: 'Card Number', field: 'Key', type: 'text' },\r\n { text: 'Transactions', field: 'DocCount', type: 'number' },\r\n { text: 'Merchants', field: 'MerchantCount', type: 'number' },\r\n { text: 'Number of Distinct Devices', field: 'DeviceCount', type: 'number' },\r\n { text: 'Failed Authorization transactions', field: 'AuthResultNDocCount', type: 'number' },\r\n { text: 'High-Risk', field: 'HighDocCount', type: 'number' },\r\n { text: 'Mid-Risk', field: 'MidDocCount', type: 'number' },\r\n { text: 'Countries/Regions', field: 'CountryCount', type: 'number' },\r\n { text: 'Source IP Counts', field: 'IpCount', type: 'number' },\r\n];\r\n\r\n// 加總欄位\r\nconst highTxTotals = {\r\n DocCount: 0,\r\n MerchantCount: 0,\r\n DeviceCount: 0,\r\n AuthResultNDocCount: 0,\r\n AuthResultNDocCount: 0,\r\n HighDocCount: 0,\r\n MidDocCount: 0,\r\n CountryCount: 0,\r\n IpCount: 0,\r\n};\r\n\r\n\r\nfunction setupHighTxTableHeader(data) {\r\n const tableHeader = document.getElementById('account-hightx-table-header');\r\n tableHeader.innerHTML = '';\r\n\r\n const headerRow = document.createElement('tr');\r\n\r\n highTxHeaders.forEach(header => {\r\n const th = document.createElement('th');\r\n th.textContent = header.text;\r\n th.dataset.field = header.field;\r\n\r\n if (header.field !== 'countryName') {\r\n th.addEventListener('click', () => {\r\n if (currentHighTxSortField === header.field) {\r\n currentHighTxOrder = currentHighTxOrder === 'asc' ? 'desc' : 'asc';\r\n } else {\r\n currentHighTxSortField = header.field;\r\n currentHighTxOrder = 'asc';\r\n }\r\n generateHighTxItems(data, currentHighTxSortField, currentHighTxOrder);\r\n updateTableHeaderSortIndicator(headers);\r\n });\r\n }\r\n\r\n headerRow.appendChild(th);\r\n });\r\n\r\n tableHeader.appendChild(headerRow);\r\n}\r\n\r\nfunction generateHighTxItems(data, sortField = '', sortOrder = 'desc') {\r\n\r\n const tableBody = document.getElementById('account-hightx-table-body');\r\n const tableFooter = document.getElementById('account-hightx-table-footer');\r\n tableBody.innerHTML = '';\r\n tableFooter.innerHTML = '';\r\n\r\n // 排序\r\n data.sort((a, b) => {\r\n const valA = a[sortField];\r\n const valB = b[sortField];\r\n\r\n const numA = Number(String(valA).replace(/,/g, ''));\r\n const numB = Number(String(valB).replace(/,/g, ''));\r\n\r\n if (!isNaN(numA) && !isNaN(numB)) {\r\n return sortOrder === 'asc' ? numA - numB : numB - numA;\r\n } else {\r\n return sortOrder === 'asc'\r\n ? String(valA).localeCompare(String(valB))\r\n : String(valB).localeCompare(String(valA));\r\n }\r\n });\r\n\r\n resetHighTxTotals();\r\n\r\n data.forEach(item => {\r\n\r\n const row = document.createElement('tr');\r\n\r\n highTxHeaders.forEach(header => {\r\n\r\n let value = item[header.field];\r\n\r\n if (header.type === 'text') {\r\n //value = value;\r\n } else if (header.type === 'number') {\r\n const numericValue = Number(String(value).replace(/,/g, '')) || 0;\r\n value = numericValue.toLocaleString();\r\n\r\n if (highTxTotals.hasOwnProperty(header.field)) {\r\n highTxTotals[header.field] += numericValue;\r\n }\r\n }\r\n row.appendChild(createCell(value));\r\n });\r\n\r\n tableBody.appendChild(row);\r\n\r\n });\r\n\r\n // // 表尾加總列\r\n const footerRow = document.createElement('tr');\r\n highTxHeaders.forEach(header => {\r\n\r\n if (highTxTotals.hasOwnProperty(header.field)) {\r\n footerRow.appendChild(createCell(highTxTotals[header.field].toLocaleString(), true));\r\n } else if (header.field === 'Key') {\r\n footerRow.appendChild(createCell('Total', true));\r\n }\r\n else {\r\n footerRow.appendChild(createCell('', true));\r\n }\r\n\r\n });\r\n\r\n tableFooter.appendChild(footerRow);\r\n}\r\n\r\nfunction createCell(text, isFooter = false) {\r\n const cell = document.createElement('td');\r\n cell.textContent = text;\r\n if (isFooter) cell.style.fontWeight = 'bold';\r\n return cell;\r\n}\r\n\r\nfunction updateTableHeaderSortIndicator(headers) {\r\n const ths = document.querySelectorAll('#account-hightx-table-header th');\r\n ths.forEach(th => {\r\n const field = th.dataset.field;\r\n const header = headers.find(h => h.field === field);\r\n if (!header) return;\r\n\r\n if (field === currentHighTxSortField) {\r\n th.textContent = `${header.text} ${currentHighTxOrder === 'asc' ? '▲' : '▼'}`;\r\n } else {\r\n th.textContent = header.text;\r\n }\r\n });\r\n}\r\n\r\nfunction resetHighTxTotals() {\r\n for (const key in highTxTotals) {\r\n if (Object.hasOwn(highTxTotals, key)) highTxTotals[key] = 0;\r\n }\r\n}\r\n\r\n// 初始化\r\nif (Array.isArray(context.data) && context.data.length > 0 && Array.isArray(context.data[0])) {\r\n const tableData = context.data[0];\r\n setupHighTxTableHeader(tableData);\r\n generateHighTxItems(tableData);\r\n}\r\n\r\n",
"content" : "<div class=\"account-hightx-table-container\">\n <table class=\"account-hightx-table\">\n <thead id=\"account-hightx-table-header\"></thead> \n <tbody id=\"account-hightx-table-body\"></tbody>\n </table>\n <p>\n <table class=\"account-hightx-table-footer account-hightx-table\">\n <tfoot id=\"account-hightx-table-footer\"></tfoot>\n </table>\n</div>",
"contentPartials" : [ ],
"defaultContent" : "The query didn't return any results.",
"editor" : {
"format" : "auto",
"language" : "markdown"
},
"editors" : [ "afterRender", "styles" ],
"externalStyles" : [ ],
"helpers" : "",
"renderMode" : "data",
"styles" : "<style>\r\naccount-hightx-table-container {\r\n width: 98%;\r\n margin: 0 auto;\r\n max-height: 180px; /* 必須設定 */\r\n overflow-y: auto; /* 讓內容可滾動 */\r\n position: relative; \r\n}\r\n\r\n/* 表格本體 */\r\n.account-hightx-table {\r\n border-collapse: collapse;\r\n width: 100%;\r\n table-layout: fixed;\r\n}\r\n\r\n.account-hightx-table-footer {\r\n position: sticky;\r\n bottom: 0;\r\n z-index: 3;\r\n pointer-events: none; /* 若不需互動 */\r\n backdrop-filter: blur(15px);\r\n}\r\n\r\n/* 固定 header 和 footer */\r\n.account-hightx-table thead th, .account-hightx-table tfoot td {\r\n position: sticky;\r\n z-index: 2;\r\n}\r\n\r\n/* 表頭固定在頂部 */\r\n.account-hightx-table thead th {\r\n top: 0;\r\n text-align: center;\r\n}\r\n\r\n/* 表尾固定在底部 */\r\n.account-hightx-table tfoot td {\r\n bottom: 0;\r\n}\r\n\r\n.account-hightx-table td:first-child,\r\n.account-hightx-table th:first-child {\r\n width: 18%;\r\n}\r\n\r\n/* 表格內容欄位 */\r\n.account-hightx-table tbody td {\r\n padding: 8px;\r\n}\r\n\r\n/* 筆數與百分比靠右 */\r\n.account-hightx-table tbody td, .account-hightx-table-footer tbody td, {\r\n text-align: right;\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.account-hightx-table tbody td:first-child,\r\n.account-hightx-table tfoot td:first-child {\r\n text-align: center;\r\n}\r\n\r\n.account-hightx-table-footer, .account-hightx-table-footer td {\r\n border: none;\r\n}\r\n.account-hightx-table-footer {\r\n border-top: 1px solid rgba(204, 204, 220, 0.2);;\r\n}\r\n\r\n/* 滑鼠提示效果 */\r\n.account-hightx-table thead th {\r\n cursor: pointer;\r\n}\r\n\r\n.account-hightx-table td {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n</style>",
"wrap" : true
},
"pluginVersion" : "5.7.0",
"targets" : [ {
"columns" : [ ],
"datasource" : {
"type" : "yesoreyeram-infinity-datasource",
"uid" : "aegaeyjq187b4e"
},
"filters" : [ ],
"format" : "table",
"global_query_id" : "",
"parser" : "backend",
"refId" : "A",
"root_selector" : "",
"source" : "url",
"type" : "json",
"url" : "/smartfds-adm-web/report/api/high-tx-aggs",
"url_options" : {
"data" : "",
"method" : "GET",
"params" : [ {
"key" : "operator_id",
"value" : "${operator_id}"
}, {
"key" : "institute_id",
"value" : "${institute_id}"
}, {
"key" : "from_time",
"value" : "${sp_from_timestamp_1}"
}, {
"key" : "to_time",
"value" : "${sp_to_timestamp_1}"
}, {
"key" : "auth_token",
"value" : "${auth_token}"
}, {
"key" : "merchant_id",
"value" : "${merchant_id}"
}, {
"key" : "aggregation_id",
"value" : "txInfo__acctNumber"
} ]
}
} ],
"title" : "D-01. Single Card High-Frequency Transaction Analysis",
"transformations" : [ {
"disabled" : true,
"id" : "organize",
"options" : {
"excludeByName" : {
"authResultN" : true,
"txAmount" : true,
"txCountUniDevice" : true
},
"includeByName" : { },
"indexByName" : {
"authResultN" : 6,
"authResultY" : 4,
"countryName" : 0,
"txAmount" : 3,
"txCount" : 1,
"txCountDevice" : 5,
"txCountUniDevice" : 7,
"txRealAmount" : 2
},
"renameByName" : {
"authResultY" : "授權成功數量",
"countryName" : "國家",
"txAmount" : "交易金額(美金)",
"txCount" : "交易數量",
"txCountDevice" : "設備數量",
"txRealAmount" : "交易金額(台幣)"
}
}
} ],
"type" : "marcusolsson-dynamictext-panel"
},
"version" : 5,
"meta" : {
"folderName" : "R98-Library",
"folderUid" : "bemeqa2prz7k0d",
"connectedDashboards" : 2,
"created" : "2025-06-26T14:04:12+08:00",
"updated" : "2025-06-27T11:01:54+08:00",
"createdBy" : {
"avatarUrl" : "/avatar/ce6412d58e966caaa26cac12eb99734b",
"id" : 1,
"name" : "admin"
},
"updatedBy" : {
"avatarUrl" : "/avatar/ce6412d58e966caaa26cac12eb99734b",
"id" : 1,
"name" : "admin"
}
}
}
}
(28-28/30)