123 lines
3.7 KiB
JavaScript
123 lines
3.7 KiB
JavaScript
|
|
export const DEFAULT_OVERVIEW_RANGE = '近10日'
|
||
|
|
|
||
|
|
const DAY_MS = 24 * 60 * 60 * 1000
|
||
|
|
const RISK_DAILY_TREND_MAX_BUCKETS = 14
|
||
|
|
|
||
|
|
function parseLocalDate(value) {
|
||
|
|
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(value || '').trim())
|
||
|
|
if (!match) {
|
||
|
|
return null
|
||
|
|
}
|
||
|
|
const date = new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]))
|
||
|
|
return Number.isNaN(date.getTime()) ? null : date
|
||
|
|
}
|
||
|
|
|
||
|
|
function clampWindowDays(value) {
|
||
|
|
const days = Number(value || 0)
|
||
|
|
if (!Number.isFinite(days) || days <= 0) {
|
||
|
|
return 10
|
||
|
|
}
|
||
|
|
return Math.max(1, Math.min(Math.round(days), 90))
|
||
|
|
}
|
||
|
|
|
||
|
|
function resolveCustomRangeDays(customRange = {}) {
|
||
|
|
const start = parseLocalDate(customRange.start)
|
||
|
|
const end = parseLocalDate(customRange.end)
|
||
|
|
if (!start || !end) {
|
||
|
|
return 10
|
||
|
|
}
|
||
|
|
return clampWindowDays(Math.abs(end.getTime() - start.getTime()) / DAY_MS + 1)
|
||
|
|
}
|
||
|
|
|
||
|
|
export function resolveTopRangeDays(range, customRange = {}) {
|
||
|
|
const key = String(range || DEFAULT_OVERVIEW_RANGE).trim()
|
||
|
|
if (key === 'custom') {
|
||
|
|
return resolveCustomRangeDays(customRange)
|
||
|
|
}
|
||
|
|
if (key === '\u4eca\u65e5') {
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
if (key === '\u672c\u5468') {
|
||
|
|
const today = new Date()
|
||
|
|
const weekday = today.getDay() || 7
|
||
|
|
return clampWindowDays(weekday)
|
||
|
|
}
|
||
|
|
if (key === '\u672c\u6708') {
|
||
|
|
return clampWindowDays(new Date().getDate())
|
||
|
|
}
|
||
|
|
const match = key.match(/\d+/)
|
||
|
|
return clampWindowDays(match ? Number(match[0]) : 10)
|
||
|
|
}
|
||
|
|
|
||
|
|
export function resolveTopRangeKey(range, customRange = {}) {
|
||
|
|
const key = String(range || DEFAULT_OVERVIEW_RANGE).trim()
|
||
|
|
if (key === 'custom') {
|
||
|
|
return 'custom'
|
||
|
|
}
|
||
|
|
if (key === '\u672c\u5468' || key === '\u4eca\u65e5') {
|
||
|
|
return `recent-${resolveTopRangeDays(key, customRange)}-days`
|
||
|
|
}
|
||
|
|
if (/\d+/.test(key)) {
|
||
|
|
return `recent-${resolveTopRangeDays(key, customRange)}-days`
|
||
|
|
}
|
||
|
|
return key || DEFAULT_OVERVIEW_RANGE
|
||
|
|
}
|
||
|
|
|
||
|
|
function formatRiskTrendDateLabel(value) {
|
||
|
|
const date = parseLocalDate(value)
|
||
|
|
if (!date) {
|
||
|
|
return String(value || '-').trim() || '-'
|
||
|
|
}
|
||
|
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||
|
|
const day = String(date.getDate()).padStart(2, '0')
|
||
|
|
return `${month}-${day}`
|
||
|
|
}
|
||
|
|
|
||
|
|
function buildRiskTrendBucketLabel(first, last) {
|
||
|
|
const start = String(first?.date || '').trim()
|
||
|
|
const end = String(last?.date || '').trim()
|
||
|
|
if (!start || start === end) {
|
||
|
|
return formatRiskTrendDateLabel(start)
|
||
|
|
}
|
||
|
|
return `${formatRiskTrendDateLabel(start)}~${formatRiskTrendDateLabel(end)}`
|
||
|
|
}
|
||
|
|
|
||
|
|
function normalizeRiskTrendRow(item) {
|
||
|
|
return {
|
||
|
|
date: String(item.date || '').trim() || '-',
|
||
|
|
total: Number(item.total || 0),
|
||
|
|
highOrAbove: Number(item.high_or_above ?? item.highOrAbove ?? 0)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function aggregateRiskDailyTrendRows(rows, maxBuckets = RISK_DAILY_TREND_MAX_BUCKETS) {
|
||
|
|
const normalizedRows = rows
|
||
|
|
.map(normalizeRiskTrendRow)
|
||
|
|
.filter((item) => item.date !== '-' || item.total > 0 || item.highOrAbove > 0)
|
||
|
|
|
||
|
|
if (normalizedRows.length <= maxBuckets) {
|
||
|
|
return normalizedRows.map((item) => ({
|
||
|
|
...item,
|
||
|
|
date: formatRiskTrendDateLabel(item.date),
|
||
|
|
sourceStartDate: item.date,
|
||
|
|
sourceEndDate: item.date
|
||
|
|
}))
|
||
|
|
}
|
||
|
|
|
||
|
|
const bucketSize = Math.ceil(normalizedRows.length / maxBuckets)
|
||
|
|
const buckets = []
|
||
|
|
for (let index = 0; index < normalizedRows.length; index += bucketSize) {
|
||
|
|
const bucketRows = normalizedRows.slice(index, index + bucketSize)
|
||
|
|
const first = bucketRows[0]
|
||
|
|
const last = bucketRows[bucketRows.length - 1]
|
||
|
|
buckets.push({
|
||
|
|
date: buildRiskTrendBucketLabel(first, last),
|
||
|
|
sourceStartDate: first?.date || '',
|
||
|
|
sourceEndDate: last?.date || '',
|
||
|
|
total: bucketRows.reduce((sum, item) => sum + item.total, 0),
|
||
|
|
highOrAbove: bucketRows.reduce((sum, item) => sum + item.highOrAbove, 0)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
return buckets
|
||
|
|
}
|