This commit is contained in:
2026-02-15 16:24:29 +08:00
parent 50c72d6989
commit 41a3ab45b3
36 changed files with 4896 additions and 2089 deletions

View File

@ -181,12 +181,319 @@ export function createTicketNo(data) {
})
}
// 概率统计
//获取概率统计 电量指标接口
export function getElectricData({siteId, startDate, endDate}) {
function getFieldNameByCode(fieldCode) {
const raw = String(fieldCode || '').trim()
if (!raw) return ''
const idx = raw.lastIndexOf('__')
return idx >= 0 ? raw.slice(idx + 2) : raw
}
function normalizeRows(displayResponse) {
const rows = displayResponse?.data || []
return Array.isArray(rows) ? rows : []
}
function filterByMenu(rows, menuCode) {
return (rows || []).filter(item => item && item.menuCode === menuCode)
}
function toDateLabel(valueTime) {
if (!valueTime) return ''
const date = new Date(valueTime)
if (isNaN(date.getTime())) return ''
const p = (n) => String(n).padStart(2, '0')
return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())}`
}
function toDateTimeLabel(valueTime) {
if (!valueTime) return ''
const date = new Date(valueTime)
if (isNaN(date.getTime())) return ''
const p = (n) => String(n).padStart(2, '0')
return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())} ${p(date.getHours())}:${p(date.getMinutes())}`
}
function toValueMap(rows) {
const map = {}
;(rows || []).forEach(item => {
const fieldName = getFieldNameByCode(item?.fieldCode)
if (fieldName) {
map[fieldName] = item?.fieldValue
}
})
return map
}
function groupRowsByDevice(rows) {
const map = new Map()
;(rows || []).forEach(item => {
const deviceId = String(item?.deviceId || '').trim() || 'DEFAULT'
if (!map.has(deviceId)) {
map.set(deviceId, [])
}
map.get(deviceId).push(item)
})
return map
}
function paginateRows(rows, pageNum = 1, pageSize = 10) {
const safePageNum = Number(pageNum) > 0 ? Number(pageNum) : 1
const safePageSize = Number(pageSize) > 0 ? Number(pageSize) : 10
const start = (safePageNum - 1) * safePageSize
return (rows || []).slice(start, start + safePageSize)
}
function toNumber(value) {
const num = Number(value)
return Number.isFinite(num) ? num : null
}
function normalizeDateInput(dateStr) {
if (dateStr) return dateStr
return toDateLabel(new Date())
}
function resolveElectricUnit(startDate, endDate) {
const start = new Date(`${normalizeDateInput(startDate)} 00:00:00`)
const end = new Date(`${normalizeDateInput(endDate)} 00:00:00`)
if (isNaN(start.getTime()) || isNaN(end.getTime())) return '日'
const diffDays = Math.floor((end.getTime() - start.getTime()) / (24 * 60 * 60 * 1000))
if (diffDays <= 0) return '时'
if (diffDays < 30) return '日'
return '月'
}
function formatByUnit(date, unit) {
const p = (n) => String(n).padStart(2, '0')
if (unit === '时') return `${p(date.getHours())}:${p(date.getMinutes())}`
if (unit === '月') return `${date.getFullYear()}-${p(date.getMonth() + 1)}`
return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())}`
}
function aggregateCurveByUnit(curveRows, unit) {
const result = new Map()
;(curveRows || []).forEach(item => {
const time = item?.dataTime ? new Date(item.dataTime) : null
if (!time || isNaN(time.getTime())) return
const value = toNumber(item?.pointValue)
if (value == null) return
const label = formatByUnit(time, unit)
result.set(label, value)
})
return result
}
function getLatestCurveValue(curveRows) {
let latestTime = null
let latestValue = null
;(curveRows || []).forEach(item => {
const time = item?.dataTime ? new Date(item.dataTime) : null
if (!time || isNaN(time.getTime())) return
const value = toNumber(item?.pointValue)
if (value == null) return
if (latestTime == null || time.getTime() > latestTime) {
latestTime = time.getTime()
latestValue = value
}
})
return latestValue
}
function findMappingByField(rows, fieldNames = []) {
const targetSet = new Set((fieldNames || []).map(name => String(name || '').trim()))
return (rows || []).find(item => targetSet.has(getFieldNameByCode(item?.fieldCode)))
}
function getDataPointFromMapping(mapping) {
if (!mapping) return ''
const useFixedDisplay = Number(mapping?.useFixedDisplay) === 1
const fixedDataPoint = String(mapping?.fixedDataPoint || '').trim()
const dataPoint = String(mapping?.dataPoint || '').trim()
if (useFixedDisplay && fixedDataPoint) return fixedDataPoint
return dataPoint || fixedDataPoint
}
function queryPointCurveByPointId({siteId, pointId, startDate, endDate}) {
if (!siteId || !pointId) return Promise.resolve([])
const start = `${normalizeDateInput(startDate)} 00:00:00`
const end = `${normalizeDateInput(endDate)} 23:59:59`
return request({
url: `/ems/statsReport/getElectricData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`,
method: 'get'
url: `/ems/pointConfig/curve`,
method: 'post',
headers: {
repeatSubmit: false
},
data: {
siteId,
pointId,
rangeType: 'custom',
startTime: start,
endTime: end
}
}).then(resp => (Array.isArray(resp?.data) ? resp.data : []))
}
function sortCurveRows(curveRows = []) {
return [...(curveRows || [])].sort((a, b) => {
const ta = new Date(a?.dataTime).getTime()
const tb = new Date(b?.dataTime).getTime()
return (isNaN(ta) ? 0 : ta) - (isNaN(tb) ? 0 : tb)
})
}
function resolveRangeKind(startDate, endDate) {
const start = new Date(`${normalizeDateInput(startDate)} 00:00:00`)
const end = new Date(`${normalizeDateInput(endDate || startDate)} 00:00:00`)
if (isNaN(start.getTime()) || isNaN(end.getTime())) return 'day'
const diffDays = Math.floor((end.getTime() - start.getTime()) / (24 * 60 * 60 * 1000))
if (diffDays <= 0) return 'minute'
if (diffDays < 30) return 'day'
return 'month'
}
function formatTimeLabelByKind(date, kind = 'day') {
const p = (n) => String(n).padStart(2, '0')
if (kind === 'minute') return `${p(date.getHours())}:${p(date.getMinutes())}`
if (kind === 'month') return `${date.getFullYear()}-${p(date.getMonth() + 1)}`
return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())}`
}
function buildSortedLabels(labelSet, kind = 'day') {
const labels = Array.from(labelSet || [])
if (kind === 'minute') {
return labels.sort((a, b) => {
const [ah = 0, am = 0] = String(a || '').split(':').map(v => Number(v) || 0)
const [bh = 0, bm = 0] = String(b || '').split(':').map(v => Number(v) || 0)
return ah * 60 + am - (bh * 60 + bm)
})
}
return labels.sort()
}
function normalizePointValue(raw) {
const num = toNumber(raw)
if (num != null) return num
return raw == null ? '' : raw
}
function resolveAliasByField(aliasMap, fieldName) {
const raw = String(fieldName || '').trim()
if (!raw) return ''
if (aliasMap[raw]) return aliasMap[raw]
const withoutStat = raw.replace(/_stat$/, '')
return aliasMap[withoutStat] || ''
}
function queryMenuPointCurves({siteId, menuCode, startDate, endDate, mappingFilter}) {
return getProjectPointMapping(siteId).then((mappingResp) => {
const allMappings = Array.isArray(mappingResp?.data) ? mappingResp.data : []
let menuMappings = allMappings.filter(item => item?.menuCode === menuCode)
if (typeof mappingFilter === 'function') {
menuMappings = menuMappings.filter(mappingFilter)
}
const tasks = menuMappings.map((mapping) => {
const pointId = getDataPointFromMapping(mapping)
if (!pointId) return Promise.resolve(null)
return queryPointCurveByPointId({siteId, pointId, startDate, endDate})
.then(curve => ({
deviceId: String(mapping?.deviceId || '').trim(),
fieldName: getFieldNameByCode(mapping?.fieldCode),
curve: sortCurveRows(curve || []),
}))
.catch(() => ({
deviceId: String(mapping?.deviceId || '').trim(),
fieldName: getFieldNameByCode(mapping?.fieldCode),
curve: [],
}))
})
return Promise.all(tasks).then(rows => rows.filter(Boolean))
})
}
// 电量指标
export function getElectricData({siteId, startDate, endDate}) {
return getProjectPointMapping(siteId).then((mappingResp) => {
const allMappings = Array.isArray(mappingResp?.data) ? mappingResp.data : []
const gltjMappings = allMappings.filter(item => item?.menuCode === 'TJBB_GLTJ')
const chargedMap = findMappingByField(gltjMappings, ['chargedCap_stat', 'chargedCap'])
const disChargedMap = findMappingByField(gltjMappings, ['disChargedCap_stat', 'disChargedCap'])
const dailyEfficiencyMap = findMappingByField(gltjMappings, ['dailyEfficiency'])
const totalChargedMap = findMappingByField(gltjMappings, ['totalChargedCap_stat', 'totalChargedCap'])
const totalDisChargedMap = findMappingByField(gltjMappings, ['totalDisChargedCap_stat', 'totalDisChargedCap'])
const totalEfficiencyMap = findMappingByField(gltjMappings, ['efficiency'])
const pointMap = {
charged: getDataPointFromMapping(chargedMap),
disCharged: getDataPointFromMapping(disChargedMap),
dailyEfficiency: getDataPointFromMapping(dailyEfficiencyMap),
totalCharged: getDataPointFromMapping(totalChargedMap),
totalDisCharged: getDataPointFromMapping(totalDisChargedMap),
totalEfficiency: getDataPointFromMapping(totalEfficiencyMap),
}
const queryTasks = Object.keys(pointMap).map((key) => {
const pointId = pointMap[key]
return queryPointCurveByPointId({siteId, pointId, startDate, endDate})
.then(curve => ({key, curve}))
.catch(() => ({key, curve: []}))
})
return Promise.all(queryTasks).then((queryResult) => {
const curveMap = {}
queryResult.forEach(item => {
curveMap[item.key] = item.curve || []
})
const unit = resolveElectricUnit(startDate, endDate)
const chargedSeries = aggregateCurveByUnit(curveMap.charged, unit)
const disChargedSeries = aggregateCurveByUnit(curveMap.disCharged, unit)
const efficiencySeries = aggregateCurveByUnit(curveMap.dailyEfficiency, unit)
const labels = Array.from(new Set([
...chargedSeries.keys(),
...disChargedSeries.keys(),
...efficiencySeries.keys(),
])).sort()
const sevenDayDisChargeStats = labels.map((label) => {
const chargedCap = chargedSeries.get(label)
const disChargedCap = disChargedSeries.get(label)
let dailyEfficiency = efficiencySeries.get(label)
if (dailyEfficiency == null && chargedCap != null && chargedCap !== 0 && disChargedCap != null) {
dailyEfficiency = Number(((disChargedCap / chargedCap) * 100).toFixed(2))
}
return {
ammeterDate: label,
chargedCap: chargedCap == null ? '' : chargedCap,
disChargedCap: disChargedCap == null ? '' : disChargedCap,
dailyEfficiency: dailyEfficiency == null ? '' : dailyEfficiency,
}
})
const fallbackTotalCharged = sevenDayDisChargeStats.reduce((acc, item) => acc + (toNumber(item.chargedCap) || 0), 0)
const fallbackTotalDisCharged = sevenDayDisChargeStats.reduce((acc, item) => acc + (toNumber(item.disChargedCap) || 0), 0)
const totalChargedCap = getLatestCurveValue(curveMap.totalCharged)
const totalDisChargedCap = getLatestCurveValue(curveMap.totalDisCharged)
const totalEfficiency = getLatestCurveValue(curveMap.totalEfficiency)
const resultTotalCharged = totalChargedCap == null ? fallbackTotalCharged : totalChargedCap
const resultTotalDisCharged = totalDisChargedCap == null ? fallbackTotalDisCharged : totalDisChargedCap
const resultEfficiency = totalEfficiency == null
? (resultTotalCharged > 0 ? Number(((resultTotalDisCharged / resultTotalCharged) * 100).toFixed(2)) : 0)
: totalEfficiency
return {
data: {
totalChargedCap: resultTotalCharged,
totalDisChargedCap: resultTotalDisCharged,
efficiency: resultEfficiency,
unit,
sevenDayDisChargeStats,
}
}
})
})
}
@ -198,27 +505,147 @@ export function getPcsNameList(siteId) {
})
}
//pcs曲线
export function getPCSData({siteId, startTime, endTime, dataType}) {
return request({
url: `/ems/statsReport/getPCSData?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}&dataType=${dataType}`,
method: 'get'
// pcs曲线
export function getPCSData({siteId, startTime, endTime}) {
const kind = resolveRangeKind(startTime, endTime)
const aliasMap = {
activePower_stat: 'activePower',
activePower: 'activePower',
reactivePower_stat: 'reactivePower',
reactivePower: 'reactivePower',
uCurrent: 'uCurrent',
vCurrent: 'vCurrent',
wCurrent: 'wCurrent',
}
return queryMenuPointCurves({
siteId,
menuCode: 'TJBB_PCSQX',
startDate: startTime,
endDate: endTime,
}).then((records) => {
const byDevice = new Map()
records.forEach((record) => {
const alias = resolveAliasByField(aliasMap, record.fieldName)
if (!alias) return
const deviceId = record.deviceId || ''
if (!byDevice.has(deviceId)) byDevice.set(deviceId, new Map())
const rowMap = byDevice.get(deviceId)
;(record.curve || []).forEach((point) => {
const time = point?.dataTime ? new Date(point.dataTime) : null
if (!time || isNaN(time.getTime())) return
const label = formatTimeLabelByKind(time, kind)
if (!rowMap.has(label)) rowMap.set(label, {statisDate: label})
rowMap.get(label)[alias] = normalizePointValue(point?.pointValue)
})
})
const data = []
byDevice.forEach((rowMap, deviceId) => {
const labels = buildSortedLabels(new Set(Array.from(rowMap.keys())), kind)
data.push({
deviceId,
dataList: labels.map(label => rowMap.get(label)),
})
})
return {data}
})
}
//电池堆曲线
export function getStackData({siteId, startTime, endTime, dataType}) {
return request({
url: `/ems/statsReport/getStackData?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}&dataType=${dataType}`,
method: 'get'
// 电池堆曲线
export function getStackData({siteId, startTime, endTime}) {
const kind = resolveRangeKind(startTime, endTime)
const aliasMap = {
temp: 'temp',
voltage_stat: 'voltage',
voltage: 'voltage',
current: 'current',
soc_stat: 'soc',
soc: 'soc',
}
return queryMenuPointCurves({
siteId,
menuCode: 'TJBB_DCDQX',
startDate: startTime,
endDate: endTime,
}).then((records) => {
const byDevice = new Map()
records.forEach((record) => {
const alias = resolveAliasByField(aliasMap, record.fieldName)
if (!alias) return
const deviceId = record.deviceId || ''
if (!byDevice.has(deviceId)) byDevice.set(deviceId, new Map())
const rowMap = byDevice.get(deviceId)
;(record.curve || []).forEach((point) => {
const time = point?.dataTime ? new Date(point.dataTime) : null
if (!time || isNaN(time.getTime())) return
const label = formatTimeLabelByKind(time, kind)
if (!rowMap.has(label)) rowMap.set(label, {statisDate: label})
rowMap.get(label)[alias] = normalizePointValue(point?.pointValue)
})
})
const data = []
byDevice.forEach((rowMap, deviceId) => {
const labels = buildSortedLabels(new Set(Array.from(rowMap.keys())), kind)
data.push({
deviceId,
dataList: labels.map(label => rowMap.get(label)),
})
})
return {data}
})
}
//电池温度
// 电池温度
export function getClusterData({siteId, stackId, clusterId, dateTime, pageNum, pageSize}) {
return request({
url: `/ems/statsReport/getClusterData?siteId=${siteId}&stackId=${stackId}&clusterId=${clusterId}&dateTime=${dateTime}&pageNum=${pageNum}&pageSize=${pageSize}`,
method: 'get'
const startDate = dateTime || normalizeDateInput('')
const endDate = dateTime || normalizeDateInput('')
const kind = 'minute'
const aliasMap = {
maxTemp: 'maxTemp',
maxTempId: 'maxTempId',
minTemp: 'minTemp',
minTempId: 'minTempId',
maxVoltage: 'maxVoltage',
maxVoltageId: 'maxVoltageId',
minVoltage: 'minVoltage',
minVoltageId: 'minVoltageId',
}
const queryClusterCurves = (withClusterFilter) => queryMenuPointCurves({
siteId,
menuCode: 'TJBB_DCWD',
startDate,
endDate,
mappingFilter: withClusterFilter
? (item) => {
if (!clusterId) return true
return String(item?.deviceId || '').trim() === String(clusterId || '').trim()
}
: undefined
})
return queryClusterCurves(true).then((records) => {
if (clusterId && (!records || records.length === 0)) {
return queryClusterCurves(false)
}
return records
}).then((records) => {
const rowMap = new Map()
records.forEach((record) => {
const alias = resolveAliasByField(aliasMap, record.fieldName)
if (!alias) return
;(record.curve || []).forEach((point) => {
const time = point?.dataTime ? new Date(point.dataTime) : null
if (!time || isNaN(time.getTime())) return
const label = formatTimeLabelByKind(time, kind)
if (!rowMap.has(label)) rowMap.set(label, {statisDate: label})
rowMap.get(label)[alias] = normalizePointValue(point?.pointValue)
})
})
const labels = buildSortedLabels(new Set(Array.from(rowMap.keys())), kind)
const fullRows = labels.map(label => rowMap.get(label))
return {
rows: paginateRows(fullRows, pageNum, pageSize),
total: fullRows.length,
}
})
}
@ -258,9 +685,37 @@ export function batteryAveTemp(siteId, startTime, endTime) {
// 功率曲线
export function getPowerData({siteId, startDate, endDate}) {
return request({
url: `/ems/statsReport/getPowerData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`,
method: 'get'
const kind = resolveRangeKind(startDate, endDate)
const aliasMap = {
gridPower_stat: 'gridPower',
gridPower: 'gridPower',
loadPower_stat: 'loadPower',
loadPower: 'loadPower',
storagePower_stat: 'storagePower',
storagePower: 'storagePower',
pvPower_stat: 'pvPower',
pvPower: 'pvPower',
}
return queryMenuPointCurves({
siteId,
menuCode: 'TJBB_GLQX',
startDate,
endDate,
}).then((records) => {
const rowMap = new Map()
records.forEach((record) => {
const alias = resolveAliasByField(aliasMap, record.fieldName)
if (!alias) return
;(record.curve || []).forEach((point) => {
const time = point?.dataTime ? new Date(point.dataTime) : null
if (!time || isNaN(time.getTime())) return
const label = formatTimeLabelByKind(time, kind)
if (!rowMap.has(label)) rowMap.set(label, {statisDate: label})
rowMap.get(label)[alias] = normalizePointValue(point?.pointValue)
})
})
const labels = buildSortedLabels(new Set(Array.from(rowMap.keys())), kind)
return {data: labels.map(label => rowMap.get(label))}
})
}
@ -274,18 +729,88 @@ export function getLoadNameList(siteId) {
// 电表报表
export function getAmmeterData({siteId, startTime, endTime, pageSize, pageNum}) {
return request({
url: `/ems/statsReport/getAmmeterData?siteId=${siteId}&startTime=${startTime}&endTime=${endTime}&pageSize=${pageSize}&pageNum=${pageNum}`,
method: 'get'
const kind = 'day'
const aliasMap = {
activePeakKwh: 'activePeakKwh',
activeHighKwh: 'activeHighKwh',
activeFlatKwh: 'activeFlatKwh',
activeValleyKwh: 'activeValleyKwh',
activeTotalKwh: 'activeTotalKwh',
reActivePeakKwh: 'reActivePeakKwh',
reActiveHighKwh: 'reActiveHighKwh',
reActiveFlatKwh: 'reActiveFlatKwh',
reActiveValleyKwh: 'reActiveValleyKwh',
reActiveTotalKwh: 'reActiveTotalKwh',
effect: 'effect',
}
return queryMenuPointCurves({
siteId,
menuCode: 'TJBB_DBBB',
startDate: startTime,
endDate: endTime,
}).then((records) => {
const rowMap = new Map()
records.forEach((record) => {
const alias = aliasMap[record.fieldName]
if (!alias) return
;(record.curve || []).forEach((point) => {
const time = point?.dataTime ? new Date(point.dataTime) : null
if (!time || isNaN(time.getTime())) return
const label = formatTimeLabelByKind(time, kind)
if (!rowMap.has(label)) rowMap.set(label, {dataTime: label})
rowMap.get(label)[alias] = normalizePointValue(point?.pointValue)
})
})
const labels = buildSortedLabels(new Set(Array.from(rowMap.keys())), kind)
const fullRows = labels.map(label => rowMap.get(label))
return {
rows: paginateRows(fullRows, pageNum, pageSize),
total: fullRows.length,
}
})
}
// 电价报表
export function getAmmeterRevenueData(data) {
return request({
url: `/ems/statsReport/getAmmeterRevenueData`,
method: 'get',
params: data
const {siteId, startTime, endTime, pageNum, pageSize} = data || {}
const kind = 'day'
const aliasMap = {
activePeakPrice: 'activePeakPrice',
activeHighPrice: 'activeHighPrice',
activeFlatPrice: 'activeFlatPrice',
activeValleyPrice: 'activeValleyPrice',
activeTotalPrice: 'activeTotalPrice',
reActivePeakPrice: 'reActivePeakPrice',
reActiveHighPrice: 'reActiveHighPrice',
reActiveFlatPrice: 'reActiveFlatPrice',
reActiveValleyPrice: 'reActiveValleyPrice',
reActiveTotalPrice: 'reActiveTotalPrice',
actualRevenue: 'actualRevenue',
}
return queryMenuPointCurves({
siteId,
menuCode: 'TJBB_SYBB',
startDate: startTime,
endDate: endTime,
}).then((records) => {
const rowMap = new Map()
records.forEach((record) => {
const alias = aliasMap[record.fieldName]
if (!alias) return
;(record.curve || []).forEach((point) => {
const time = point?.dataTime ? new Date(point.dataTime) : null
if (!time || isNaN(time.getTime())) return
const label = formatTimeLabelByKind(time, kind)
if (!rowMap.has(label)) rowMap.set(label, {dataTime: label})
rowMap.get(label)[alias] = normalizePointValue(point?.pointValue)
})
})
const labels = buildSortedLabels(new Set(Array.from(rowMap.keys())), kind)
const fullRows = labels.map(label => rowMap.get(label))
return {
rows: paginateRows(fullRows, pageNum, pageSize),
total: fullRows.length,
}
})
}