Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Q
qingdaoMuseumPlatform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
node_server
qingdaoMuseumPlatform
Commits
027f2027
Commit
027f2027
authored
Jul 29, 2026
by
PC-20251223ZVQQ\Administrator
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
运行分析响应速度优化
parent
addb30b5
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
255 additions
and
274 deletions
+255
-274
running.ts
src/biz/running.ts
+255
-274
No files found.
src/biz/running.ts
View file @
027f2027
...
@@ -50,7 +50,6 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -50,7 +50,6 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
}
}
let
regionInfo
:
any
=
await
selectDataListByParam
(
TABLENAME
.
区域表
,
regionParam
,
[
"id"
]);
let
regionInfo
:
any
=
await
selectDataListByParam
(
TABLENAME
.
区域表
,
regionParam
,
[
"id"
]);
let
regionKeys
=
regionInfo
.
data
.
map
((
r
:
any
)
=>
r
.
id
);
let
regionKeys
=
regionInfo
.
data
.
map
((
r
:
any
)
=>
r
.
id
);
console
.
log
(
"筛选区域Key:"
,
regionKeys
.
length
);
// 1.1 获取所选区域三相电表及电能监测设备的用电数据(用于能耗管理)
// 1.1 获取所选区域三相电表及电能监测设备的用电数据(用于能耗管理)
let
powerParams
:
any
=
{
device_type
:
{
"%in%"
:
[
"三相电表"
,
"电能监测"
]
}};
let
powerParams
:
any
=
{
device_type
:
{
"%in%"
:
[
"三相电表"
,
"电能监测"
]
}};
...
@@ -60,7 +59,8 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -60,7 +59,8 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
let
energyDevices
=
await
selectDataListByParam
(
TABLENAME
.
设备表
,
powerParams
,
[
"device_id"
]);
let
energyDevices
=
await
selectDataListByParam
(
TABLENAME
.
设备表
,
powerParams
,
[
"device_id"
]);
let
energyDeviceIds
=
energyDevices
.
data
.
map
((
d
:
any
)
=>
d
.
device_id
);
let
energyDeviceIds
=
energyDevices
.
data
.
map
((
d
:
any
)
=>
d
.
device_id
);
// 1.2 获取今日、昨日、本月、上月用电量
// === 前提:公共时间变量,后续并行模块都会用到 ===
let
nowTime
=
new
Date
();
let
todayStart
=
new
Date
();
todayStart
.
setHours
(
0
,
0
,
0
,
0
);
let
todayStart
=
new
Date
();
todayStart
.
setHours
(
0
,
0
,
0
,
0
);
let
todayEnd
=
new
Date
();
let
todayEnd
=
new
Date
();
let
yesterdayStart
=
new
Date
(
Date
.
now
()
-
86400000
);
yesterdayStart
.
setHours
(
0
,
0
,
0
,
0
);
let
yesterdayStart
=
new
Date
(
Date
.
now
()
-
86400000
);
yesterdayStart
.
setHours
(
0
,
0
,
0
,
0
);
...
@@ -68,58 +68,98 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -68,58 +68,98 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
let
currentMonthStart
=
new
Date
();
currentMonthStart
.
setDate
(
1
);
currentMonthStart
.
setHours
(
0
,
0
,
0
,
0
);
let
currentMonthStart
=
new
Date
();
currentMonthStart
.
setDate
(
1
);
currentMonthStart
.
setHours
(
0
,
0
,
0
,
0
);
let
lastMonthStart
=
new
Date
();
lastMonthStart
.
setMonth
(
lastMonthStart
.
getMonth
()
-
1
);
lastMonthStart
.
setDate
(
1
);
lastMonthStart
.
setHours
(
0
,
0
,
0
,
0
);
let
lastMonthStart
=
new
Date
();
lastMonthStart
.
setMonth
(
lastMonthStart
.
getMonth
()
-
1
);
lastMonthStart
.
setDate
(
1
);
lastMonthStart
.
setHours
(
0
,
0
,
0
,
0
);
let
lastMonthEnd
=
new
Date
(
currentMonthStart
.
getTime
()
-
1
);
let
lastMonthEnd
=
new
Date
(
currentMonthStart
.
getTime
()
-
1
);
let
last24hStart
=
new
Date
(
nowTime
.
getTime
()
-
24
*
3600000
);
let
last7DaysStart
=
new
Date
(
nowTime
.
getTime
()
-
7
*
86400000
);
let
last30DaysStart
=
new
Date
(
nowTime
.
getTime
()
-
30
*
86400000
);
let
lastYearStart
=
new
Date
(
nowTime
.
getFullYear
()
-
1
,
nowTime
.
getMonth
(),
1
);
// 辅助函数:查询某个时间段内的总用电量(单位 kwh)
// === 并行执行:能耗、环境、预警、客流 四个独立模块 ===
async
function
getTotalEnergy
(
deviceIds
:
string
[],
startTime
:
Date
,
endTime
:
Date
):
Promise
<
number
>
{
const
parallelResults
=
await
Promise
.
all
([
if
(
deviceIds
.
length
===
0
)
return
0
;
// 模块1:能耗管理
let
records
=
await
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
(
async
()
=>
{
device_id
:
{
"%in%"
:
deviceIds
},
// 一次查询覆盖所有时间范围
device_time
:
{
"%gte%"
:
formatLocalTime
(
startTime
),
"%lte%"
:
formatLocalTime
(
endTime
)
},
let
allEnergyRecords
=
await
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
device_id
:
{
"%in%"
:
energyDeviceIds
},
device_time
:
{
"%gte%"
:
formatLocalTime
(
lastYearStart
),
"%lte%"
:
formatLocalTime
(
nowTime
)
},
"%orderAsc%"
:
"device_time"
"%orderAsc%"
:
"device_time"
},
[
"device_id"
,
"data"
,
"device_time"
]);
},
[
"device_id"
,
"data"
,
"device_time"
]);
let
total
=
0
;
// 一次遍历,同时产出:4个时段总量 + 4组趋势数据
let
deviceEnergyMap
=
new
Map
();
let
todayTotal
=
0
,
yesterdayTotal
=
0
,
currentMonthTotal
=
0
,
previousMonthTotal
=
0
;
for
(
let
rec
of
records
.
data
)
{
let
deviceTodayMap
=
new
Map
<
string
,
number
>
();
let
data
=
rec
.
data
;
let
deviceYesterdayMap
=
new
Map
<
string
,
number
>
();
// let energy = data.energy ?? data.total_energy;
let
deviceMonthMap
=
new
Map
<
string
,
number
>
();
let
energy
=
data
.
current
;
let
devicePrevMonthMap
=
new
Map
<
string
,
number
>
();
let
hourlyMap
=
new
Map
<
string
,
number
>
();
let
daily7Map
=
new
Map
<
string
,
number
>
();
let
daily30Map
=
new
Map
<
string
,
number
>
();
let
monthlyMap
=
new
Map
<
string
,
number
>
();
const
padFn
=
(
n
:
number
)
=>
String
(
n
).
padStart
(
2
,
'0'
);
for
(
let
rec
of
allEnergyRecords
.
data
)
{
let
energy
=
rec
.
data
?.
current
;
if
(
energy
===
undefined
)
continue
;
if
(
energy
===
undefined
)
continue
;
if
(
!
deviceEnergyMap
.
has
(
rec
.
device_id
))
{
let
d
=
new
Date
(
rec
.
device_time
);
// deviceEnergyMap.set(rec.device_id, { min: energy, max: energy });
let
ts
=
d
.
getTime
();
deviceEnergyMap
.
set
(
rec
.
device_id
,
energy
);
let
deviceId
=
rec
.
device_id
;
}
else
{
let
cur
=
deviceEnergyMap
.
get
(
rec
.
device_id
);
// --- 总量分组 ---
// if (energy < cur.min) cur.min = energy;
if
(
ts
>=
todayStart
.
getTime
())
{
// if (energy > cur.max) cur.max = energy;
deviceTodayMap
.
set
(
deviceId
,
(
deviceTodayMap
.
get
(
deviceId
)
||
0
)
+
energy
);
energy
+=
cur
;
deviceEnergyMap
.
set
(
rec
.
device_id
,
energy
);
}
}
if
(
ts
>=
yesterdayStart
.
getTime
()
&&
ts
<=
yesterdayEnd
.
getTime
())
{
deviceYesterdayMap
.
set
(
deviceId
,
(
deviceYesterdayMap
.
get
(
deviceId
)
||
0
)
+
energy
);
}
}
for
(
let
energy
of
deviceEnergyMap
.
values
())
{
if
(
ts
>=
currentMonthStart
.
getTime
())
{
total
+=
energy
;
deviceMonthMap
.
set
(
deviceId
,
(
deviceMonthMap
.
get
(
deviceId
)
||
0
)
+
energy
)
;
}
}
return
total
;
if
(
ts
>=
lastMonthStart
.
getTime
()
&&
ts
<=
lastMonthEnd
.
getTime
())
{
devicePrevMonthMap
.
set
(
deviceId
,
(
devicePrevMonthMap
.
get
(
deviceId
)
||
0
)
+
energy
);
}
}
let
todayEnergy
=
await
getTotalEnergy
(
energyDeviceIds
,
todayStart
,
todayEnd
);
// --- 趋势分组 ---
let
yesterdayEnergy
=
await
getTotalEnergy
(
energyDeviceIds
,
yesterdayStart
,
yesterdayEnd
);
if
(
ts
>=
last24hStart
.
getTime
())
{
let
currentMonthEnergy
=
await
getTotalEnergy
(
energyDeviceIds
,
currentMonthStart
,
new
Date
());
let
key
=
`
${
d
.
getFullYear
()}
-
${
padFn
(
d
.
getMonth
()
+
1
)}
-
${
padFn
(
d
.
getDate
())}
${
padFn
(
d
.
getHours
())}
时`
;
let
previousMonthEnergy
=
await
getTotalEnergy
(
energyDeviceIds
,
lastMonthStart
,
lastMonthEnd
);
hourlyMap
.
set
(
key
,
(
hourlyMap
.
get
(
key
)
||
0
)
+
energy
);
}
if
(
ts
>=
last7DaysStart
.
getTime
())
{
let
key
=
`
${
d
.
getFullYear
()}
/
${
padFn
(
d
.
getMonth
()
+
1
)}
/
${
padFn
(
d
.
getDate
())}
`
;
daily7Map
.
set
(
key
,
(
daily7Map
.
get
(
key
)
||
0
)
+
energy
);
}
if
(
ts
>=
last30DaysStart
.
getTime
())
{
let
key
=
`
${
d
.
getFullYear
()}
/
${
padFn
(
d
.
getMonth
()
+
1
)}
/
${
padFn
(
d
.
getDate
())}
`
;
daily30Map
.
set
(
key
,
(
daily30Map
.
get
(
key
)
||
0
)
+
energy
);
}
let
monthKey
=
`
${
d
.
getFullYear
()}
-
${
padFn
(
d
.
getMonth
()
+
1
)}
`
;
monthlyMap
.
set
(
monthKey
,
(
monthlyMap
.
get
(
monthKey
)
||
0
)
+
energy
);
}
let
dailyYearOnYear
=
yesterdayEnergy
===
0
?
0
:
(((
todayEnergy
-
yesterdayEnergy
)
/
yesterdayEnergy
)
*
100
).
toFixed
(
2
);
// 汇总各时段总量
let
monthlyYearOnYear
=
previousMonthEnergy
===
0
?
0
:
(((
currentMonthEnergy
-
previousMonthEnergy
)
/
previousMonthEnergy
)
*
100
).
toFixed
(
2
);
for
(
let
v
of
deviceTodayMap
.
values
())
todayTotal
+=
v
;
for
(
let
v
of
deviceYesterdayMap
.
values
())
yesterdayTotal
+=
v
;
for
(
let
v
of
deviceMonthMap
.
values
())
currentMonthTotal
+=
v
;
for
(
let
v
of
devicePrevMonthMap
.
values
())
previousMonthTotal
+=
v
;
// 1.3 用电趋势:过去24小时(按小时)、过去7天、过去30天、过去一年(按月)
// map 转为排序后的数组
let
nowTime
=
new
Date
();
let
hourlyEnergy
:
{
key
:
string
,
value
:
string
}[]
=
[];
let
last24hStart
=
new
Date
(
nowTime
.
getTime
()
-
24
*
3600000
);
hourlyMap
.
forEach
((
v
,
k
)
=>
hourlyEnergy
.
push
({
key
:
k
,
value
:
v
.
toFixed
(
0
)
}));
let
hourlyEnergy
=
await
getEnergyTrend
(
energyDeviceIds
,
last24hStart
,
nowTime
,
'hour'
);
hourlyEnergy
.
sort
((
a
,
b
)
=>
a
.
key
.
localeCompare
(
b
.
key
));
let
last7DaysStart
=
new
Date
(
nowTime
.
getTime
()
-
7
*
86400000
);
let
dailyEnergy7
=
await
getEnergyTrend
(
energyDeviceIds
,
last7DaysStart
,
nowTime
,
'day'
);
let
dailyEnergy7
:
{
key
:
string
,
value
:
string
}[]
=
[];
let
last30DaysStart
=
new
Date
(
nowTime
.
getTime
()
-
30
*
86400000
);
daily7Map
.
forEach
((
v
,
k
)
=>
dailyEnergy7
.
push
({
key
:
k
,
value
:
v
.
toFixed
(
0
)
}));
let
dailyEnergy30
=
await
getEnergyTrend
(
energyDeviceIds
,
last30DaysStart
,
nowTime
,
'day'
);
dailyEnergy7
.
sort
((
a
,
b
)
=>
a
.
key
.
localeCompare
(
b
.
key
));
let
lastYearStart
=
new
Date
(
nowTime
.
getFullYear
()
-
1
,
nowTime
.
getMonth
(),
1
);
let
monthlyEnergyYear
=
await
getEnergyTrend
(
energyDeviceIds
,
lastYearStart
,
nowTime
,
'month'
);
let
dailyEnergy30
:
{
key
:
string
,
value
:
string
}[]
=
[];
daily30Map
.
forEach
((
v
,
k
)
=>
dailyEnergy30
.
push
({
key
:
k
,
value
:
v
.
toFixed
(
0
)
}));
dailyEnergy30
.
sort
((
a
,
b
)
=>
a
.
key
.
localeCompare
(
b
.
key
));
let
monthlyEnergyYear
:
{
key
:
string
,
value
:
string
}[]
=
[];
monthlyMap
.
forEach
((
v
,
k
)
=>
monthlyEnergyYear
.
push
({
key
:
k
,
value
:
v
.
toFixed
(
0
)
}));
monthlyEnergyYear
.
sort
((
a
,
b
)
=>
a
.
key
.
localeCompare
(
b
.
key
));
let
dailyYearOnYear
=
yesterdayTotal
===
0
?
0
:
(((
todayTotal
-
yesterdayTotal
)
/
yesterdayTotal
)
*
100
).
toFixed
(
2
);
let
monthlyYearOnYear
=
previousMonthTotal
===
0
?
0
:
(((
currentMonthTotal
-
previousMonthTotal
)
/
previousMonthTotal
)
*
100
).
toFixed
(
2
);
// 补全 x 轴坐标的工具函数
// 补全 x 轴坐标的工具函数
function
padTrend
(
trend
:
any
[],
allKeys
:
string
[]):
any
[]
{
function
padTrend
(
trend
:
any
[],
allKeys
:
string
[]):
any
[]
{
...
@@ -155,11 +195,11 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -155,11 +195,11 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
// 1.4. 能耗监控数据和能耗监控趋势图数据
// 1.4. 能耗监控数据和能耗监控趋势图数据
resultAny
.
energyManagement
=
{
resultAny
.
energyManagement
=
{
todayElectricity
:
todayEnergy
.
toFixed
(
0
),
todayElectricity
:
todayTotal
.
toFixed
(
0
),
yesterdayElectricity
:
yesterdayEnergy
.
toFixed
(
0
),
yesterdayElectricity
:
yesterdayTotal
.
toFixed
(
0
),
dailyYearOnYear
,
dailyYearOnYear
,
currentMonthElectricity
:
currentMonthEnergy
.
toFixed
(
0
),
currentMonthElectricity
:
currentMonthTotal
.
toFixed
(
0
),
previousMonthElectricity
:
previousMonthEnergy
.
toFixed
(
0
),
previousMonthElectricity
:
previousMonthTotal
.
toFixed
(
0
),
monthlyYearOnYear
monthlyYearOnYear
};
};
resultAny
.
electricityTrend
=
{
resultAny
.
electricityTrend
=
{
...
@@ -174,10 +214,11 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -174,10 +214,11 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
};
};
// 1.5. 过去24时运行能耗
// 1.5. 过去24时运行能耗
let
last24hRunningEnergy
=
hourlyEnergy
.
reduce
((
sum
:
number
,
item
:
any
)
=>
sum
+
Number
(
item
.
value
),
0
)
;
return
{
last24hRunningEnergy
:
hourlyEnergy
.
reduce
((
sum
:
number
,
item
:
any
)
=>
sum
+
Number
(
item
.
value
),
0
)
}
;
})(),
// 4.1 环境监测九合一
// 模块2:环境监测
(
async
()
=>
{
let
airParams
:
any
=
{
device_type
:
"环境监测"
};
let
airParams
:
any
=
{
device_type
:
"环境监测"
};
if
(
regionGroup
)
{
if
(
regionGroup
)
{
airParams
.
region_key
=
{
"%in%"
:
regionKeys
};
airParams
.
region_key
=
{
"%in%"
:
regionKeys
};
...
@@ -186,39 +227,51 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -186,39 +227,51 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
let
airDeviceIds
=
airQualityDevices
.
data
.
map
(
d
=>
d
.
device_id
);
let
airDeviceIds
=
airQualityDevices
.
data
.
map
(
d
=>
d
.
device_id
);
let
qualityIndex
=
300
;
let
qualityIndex
=
300
;
// 今日环境质量:{co2:660,hcho:0.01,humidity:70.5,light_level:0,pir:1,pm10:33,pm2_5:31,pressure:1011.7,temperature:21.6,tvoc:1}
let
todayEnvironmental
=
'优秀'
;
let
todayEnvironmental
=
'优秀'
;
let
currentCo2
=
0
;
// 当前二氧化氮
let
currentCo2
=
0
;
let
co2Trend
=
[];
let
co2Trend
=
[];
let
currentTemperature
=
'0℃'
;
// 当前温度
let
currentTemperature
=
'0℃'
;
let
temperatureTrend
=
[];
let
temperatureTrend
=
[];
let
currentHumidity
=
'0%'
;
// 当前湿度
let
currentHumidity
=
'0%'
;
let
humidityTrend
=
[];
let
humidityTrend
=
[];
let
currentPm25
=
0
;
// 当前PM2.5
let
currentPm25
=
0
;
let
pm25Trend
=
[];
let
pm25Trend
=
[];
let
co2TrendDetail
=
[];
let
co2TrendDetail
=
[];
let
currentHcho
=
0
;
// 当前甲醛浓度
let
currentHcho
=
0
;
let
hchoTrend
=
[];
let
hchoTrend
=
[];
let
currentLightLevel
=
0
;
// 当前光照强度
let
currentLightLevel
=
0
;
let
lightLevelTrend
=
[];
let
lightLevelTrend
=
[];
let
currentPir
=
''
;
// 当前红外感应
let
currentPir
=
''
;
let
pirTrend
=
[];
let
pirTrend
=
[];
let
currentPm10
=
0
;
// 当前PM10
let
currentPm10
=
0
;
let
pm10Trend
=
[];
let
pm10Trend
=
[];
let
currentPressure
=
0
;
// 当前大气压强
let
currentPressure
=
0
;
let
pressureTrend
=
[];
let
pressureTrend
=
[];
let
currentTvoc
=
0
;
// 当前有机物浓度
let
currentTvoc
=
0
;
let
tvocTrend
=
[];
let
tvocTrend
=
[];
if
(
airDeviceIds
.
length
)
{
if
(
airDeviceIds
.
length
)
{
// 最新数据
// 并行查询每个环境监测设备的最新一条数据(拆分 IN+ORDER BY 为 N 个精准索引查询)
let
latestAir
=
await
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
let
latestResults
=
await
Promise
.
all
(
device_id
:
{
"%in%"
:
airDeviceIds
},
airDeviceIds
.
map
((
deviceId
:
string
)
=>
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
device_id
:
deviceId
,
"%orderDesc%"
:
"device_time"
,
"%orderDesc%"
:
"device_time"
,
"%limit%"
:
1
"%limit%"
:
1
},
[
"data"
]);
},
[
"data"
,
"device_time"
])
)
);
// 找出时间最新的一条记录
let
latestAir
:
any
=
null
;
for
(
let
res
of
latestResults
)
{
if
(
res
.
data
.
length
>
0
)
{
if
(
!
latestAir
||
new
Date
(
res
.
data
[
0
].
device_time
)
>
new
Date
(
latestAir
.
data
[
0
].
device_time
))
{
latestAir
=
res
;
}
}
}
if
(
latestAir
.
data
.
length
)
{
if
(
latestAir
.
data
.
length
)
{
let
airData
=
latestAir
.
data
[
0
].
data
;
let
airData
=
latestAir
.
data
[
0
].
data
;
currentTemperature
=
`
${
airData
.
temperature
??
0
}
℃
`;
currentTemperature
=
`
${
airData
.
temperature
??
0
}
℃
`;
...
@@ -233,12 +286,11 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -233,12 +286,11 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
currentPressure = airData.pressure??0;
currentPressure = airData.pressure??0;
currentTvoc = airData.tvoc??0;
currentTvoc = airData.tvoc??0;
}
}
// 今天 00:00 ~ 当前时刻
let todayStartEnv = new Date(nowTime);
let todayStart = new Date(nowTime);
todayStartEnv.setHours(0, 0, 0, 0);
todayStart.setHours(0, 0, 0, 0);
let airHistory = await selectDataListByParam(TABLENAME.设备数据表, {
let airHistory = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": airDeviceIds },
device_id: { "%in%": airDeviceIds },
device_time: { "%gte%": formatLocalTime(todayStart
), "%lte%": formatLocalTime(nowTime) }
device_time: { "%gte%": formatLocalTime(todayStartEnv
), "%lte%": formatLocalTime(nowTime) }
}, ["data", "device_time"]);
}, ["data", "device_time"]);
let hourlyData = new Map();
let hourlyData = new Map();
for (let rec of airHistory.data) {
for (let rec of airHistory.data) {
...
@@ -255,7 +307,6 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -255,7 +307,6 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
let key = `
$
{
i
}
时
`;
let key = `
$
{
i
}
时
`;
let val = hourlyData.get(key);
let val = hourlyData.get(key);
if (i <= currentHour) {
if (i <= currentHour) {
// 已过去的小时:有数据则用,无数据填 '0'
temperatureTrend.push({ time: `
$
{
i
}:
00
`, value: val?.temp?.toString() ?? '0' });
temperatureTrend.push({ time: `
$
{
i
}:
00
`, value: val?.temp?.toString() ?? '0' });
humidityTrend.push({ time: `
$
{
i
}:
00
`, value: val?.hum?.toString() ?? '0' });
humidityTrend.push({ time: `
$
{
i
}:
00
`, value: val?.hum?.toString() ?? '0' });
pm25Trend.push({ time: `
$
{
i
}:
00
`, value: val?.pm?.toString() ?? '0' });
pm25Trend.push({ time: `
$
{
i
}:
00
`, value: val?.pm?.toString() ?? '0' });
...
@@ -269,7 +320,6 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -269,7 +320,6 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
pressureTrend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
val
?.
pressure
?.
toString
()
??
'0'
});
pressureTrend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
val
?.
pressure
?.
toString
()
??
'0'
});
tvocTrend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
val
?.
tvoc
?.
toString
()
??
'0'
});
tvocTrend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
val
?.
tvoc
?.
toString
()
??
'0'
});
}
else
{
}
else
{
// 未来的小时:填充 null,图表不画折线
temperatureTrend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
null
});
temperatureTrend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
null
});
humidityTrend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
null
});
humidityTrend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
null
});
pm25Trend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
null
});
pm25Trend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
null
});
...
@@ -285,20 +335,13 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -285,20 +335,13 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
}
}
}
}
// 计算今日环境质量
if
(
currentCo2
&&
currentPm25
)
{
if
(
currentCo2
&&
currentPm25
)
{
// 优秀:CO2< 800ppm; PM2.5≤35; 甲醛≤0.055
todayEnvironmental
=
currentCo2
<
800
&&
currentPm25
<=
35
?
'优秀'
:
todayEnvironmental
=
currentCo2
<
800
&&
currentPm25
<=
35
?
'优秀'
:
// 优秀:CO2< 1000ppm; PM2.5≤45; TVOC≤0.4
currentCo2
<
1000
&&
currentPm25
<=
45
?
'优秀'
:
currentCo2
<
1000
&&
currentPm25
<=
45
?
'优秀'
:
// 良好:CO2≤ 1000ppm; PM2.5≤50; 甲醛≤0.08
currentCo2
<=
1000
&&
currentPm25
<=
50
?
'良好'
:
currentCo2
<=
1000
&&
currentPm25
<=
50
?
'良好'
:
// 一般:CO2:1000~1500; PM2.5:50~75
currentCo2
>
1000
&&
currentCo2
<
1500
&&
currentPm25
>
50
&&
currentPm25
<
75
?
'一般'
:
'差'
currentCo2
>
1000
&&
currentCo2
<
1500
&&
currentPm25
>
50
&&
currentPm25
<
75
?
'一般'
:
'差'
}
}
// 4.2. 环境监测趋势图数据
resultAny
.
environmentalTrend
=
{
resultAny
.
environmentalTrend
=
{
todayEnvironmental
:
todayEnvironmental
,
todayEnvironmental
:
todayEnvironmental
,
currentTemperature
:
currentTemperature
,
currentTemperature
:
currentTemperature
,
...
@@ -322,29 +365,27 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -322,29 +365,27 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
pressureTrend
:
pressureTrend
,
pressureTrend
:
pressureTrend
,
tvocTrend
:
tvocTrend
tvocTrend
:
tvocTrend
};
};
})(),
// 5.1 预警监控和预警工单处理进度分析(从 device_fault 表获取,近30天)
// 模块3:预警监控
(
async
()
=>
{
const
faultStatusTextMap
:
{
[
key
:
number
]:
string
}
=
{
0
:
"未处理"
,
1
:
"已响应"
,
2
:
"已解决"
};
const
faultStatusTextMap
:
{
[
key
:
number
]:
string
}
=
{
0
:
"未处理"
,
1
:
"已响应"
,
2
:
"已解决"
};
// 降级默认值:查询或统计失败时使用,保证其他模块不受影响
let
alertStatus
=
{
totalAlerts
:
0
,
resolved
:
0
,
responded
:
0
,
unresolved
:
0
};
let
alertStatus
=
{
totalAlerts
:
0
,
resolved
:
0
,
responded
:
0
,
unresolved
:
0
};
let
alertWorkLevel
:
{
[
key
:
string
]:
number
}
=
{
"一级预警"
:
0
,
"二级预警"
:
0
,
"三级预警"
:
0
};
let
alertWorkLevel
:
{
[
key
:
string
]:
number
}
=
{
"一级预警"
:
0
,
"二级预警"
:
0
,
"三级预警"
:
0
};
let
alertWorkType
:
any
[]
=
[];
let
alertWorkType
:
any
[]
=
[];
let
alertWorkOrders
:
any
[]
=
[];
let
alertWorkOrders
:
any
[]
=
[];
try
{
try
{
// 时间范围:近30天
const
thirtyDaysAgo
=
new
Date
(
nowTime
.
getTime
()
-
30
*
86400000
);
const
thirtyDaysAgo
=
new
Date
(
nowTime
.
getTime
()
-
30
*
86400000
);
const
timeFilter
=
{
"%gte%"
:
formatLocalTime
(
thirtyDaysAgo
)
};
const
timeFilter
=
{
"%gte%"
:
formatLocalTime
(
thirtyDaysAgo
)
};
// ---- 统计查询:只取 status/level/fault_type,避免传输无用字段 ----
const
statsRes
=
await
selectDataListByParam
(
TABLENAME
.
设备故障表
,
const
statsRes
=
await
selectDataListByParam
(
TABLENAME
.
设备故障表
,
{
occurred_time
:
timeFilter
},
{
occurred_time
:
timeFilter
},
[
"status"
,
"level"
,
"fault_type"
]);
[
"status"
,
"level"
,
"fault_type"
]);
const
statsFaults
:
any
[]
=
Array
.
isArray
(
statsRes
.
data
)
?
statsRes
.
data
:
[];
const
statsFaults
:
any
[]
=
Array
.
isArray
(
statsRes
.
data
)
?
statsRes
.
data
:
[];
const
totalAlerts
=
statsFaults
.
length
;
const
totalAlerts
=
statsFaults
.
length
;
// alertStatus:按处理状态一次遍历统计
let
resolved
=
0
,
responded
=
0
,
unresolved
=
0
;
let
resolved
=
0
,
responded
=
0
,
unresolved
=
0
;
for
(
const
f
of
statsFaults
)
{
for
(
const
f
of
statsFaults
)
{
if
(
f
.
status
===
2
)
resolved
++
;
if
(
f
.
status
===
2
)
resolved
++
;
...
@@ -353,14 +394,12 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -353,14 +394,12 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
}
}
alertStatus
=
{
totalAlerts
,
resolved
,
responded
,
unresolved
};
alertStatus
=
{
totalAlerts
,
resolved
,
responded
,
unresolved
};
// alertWorkLevel:按故障等级统计
for
(
const
f
of
statsFaults
)
{
for
(
const
f
of
statsFaults
)
{
if
(
f
.
level
===
1
)
alertWorkLevel
[
"一级预警"
]
++
;
if
(
f
.
level
===
1
)
alertWorkLevel
[
"一级预警"
]
++
;
else
if
(
f
.
level
===
2
)
alertWorkLevel
[
"二级预警"
]
++
;
else
if
(
f
.
level
===
2
)
alertWorkLevel
[
"二级预警"
]
++
;
else
if
(
f
.
level
===
3
)
alertWorkLevel
[
"三级预警"
]
++
;
else
if
(
f
.
level
===
3
)
alertWorkLevel
[
"三级预警"
]
++
;
}
}
// alertWorkType:按故障类型统计
let
faultTypeCountMap
=
new
Map
<
string
,
number
>
();
let
faultTypeCountMap
=
new
Map
<
string
,
number
>
();
for
(
const
f
of
statsFaults
)
{
for
(
const
f
of
statsFaults
)
{
const
typeName
=
f
.
fault_type
||
"其他"
;
const
typeName
=
f
.
fault_type
||
"其他"
;
...
@@ -374,7 +413,6 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -374,7 +413,6 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
});
});
}
}
// ---- 工单列表查询:DB 层倒序 + LIMIT 10,避免全量拉到内存排序 ----
const
ordersRes
=
await
selectDataListByParam
(
TABLENAME
.
设备故障表
,
const
ordersRes
=
await
selectDataListByParam
(
TABLENAME
.
设备故障表
,
{
{
occurred_time
:
timeFilter
,
occurred_time
:
timeFilter
,
...
@@ -384,7 +422,6 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -384,7 +422,6 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
[
"id"
,
"device_id"
,
"fault_type"
,
"fault_code"
,
"fault_description"
,
"occurred_time"
,
"level"
,
"status"
]);
[
"id"
,
"device_id"
,
"fault_type"
,
"fault_code"
,
"fault_description"
,
"occurred_time"
,
"level"
,
"status"
]);
const
sortedFaults
:
any
[]
=
Array
.
isArray
(
ordersRes
.
data
)
?
[...
ordersRes
.
data
]
:
[];
const
sortedFaults
:
any
[]
=
Array
.
isArray
(
ordersRes
.
data
)
?
[...
ordersRes
.
data
]
:
[];
// 三级页面需要展示 alertAddress,需查设备表和区域表
let
deviceRegionMap
=
new
Map
<
string
,
string
>
();
let
deviceRegionMap
=
new
Map
<
string
,
string
>
();
if
(
regionType
&&
sortedFaults
.
length
>
0
)
{
if
(
regionType
&&
sortedFaults
.
length
>
0
)
{
const
faultUniqueDeviceIds
=
[...
new
Set
(
sortedFaults
.
map
((
f
:
any
)
=>
f
.
device_id
))];
const
faultUniqueDeviceIds
=
[...
new
Set
(
sortedFaults
.
map
((
f
:
any
)
=>
f
.
device_id
))];
...
@@ -435,146 +472,138 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -435,146 +472,138 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
alertWorkType
,
alertWorkType
,
alertWorkOrders
,
alertWorkOrders
,
};
};
})(),
// 6.1 人流监控及客流量趋势分析
// 模块4:客流监控(批量查询优化:N 次 per-region 查询 → 3 次 DB 查询)
(
async
()
=>
{
let
regionInfos
:
any
=
[];
let
regionInfos
:
any
=
[];
if
(
regionKeys
.
length
===
0
)
{
if
(
regionKeys
.
length
===
0
)
{
// 6.1.1 若没有选择区域,则查询区域表获取所有区域类型
regionInfos
=
await
selectDataListByParam
(
TABLENAME
.
区域表
,
{
"%orderAsc%"
:
"sort_order"
},
[
"id"
,
"room_id"
,
"name"
,
"type"
,
"groups"
]);
regionInfos
=
await
selectDataListByParam
(
TABLENAME
.
区域表
,
{
"%orderAsc%"
:
"sort_order"
},
[
"id"
,
"room_id"
,
"name"
,
"type"
,
"groups"
]);
}
else
{
}
else
{
// 6.1.2 若选择了区域,则查询选择的区域
regionInfos
=
await
selectDataListByParam
(
TABLENAME
.
区域表
,
{
id
:
{
"%in%"
:
regionKeys
},
"%orderAsc%"
:
"sort_order"
},
[
"id"
,
"room_id"
,
"name"
,
"type"
,
"groups"
]);
regionInfos
=
await
selectDataListByParam
(
TABLENAME
.
区域表
,
{
id
:
{
"%in%"
:
regionKeys
},
"%orderAsc%"
:
"sort_order"
},
[
"id"
,
"room_id"
,
"name"
,
"type"
,
"groups"
]);
}
}
// 6.2 循环区域查询客流传感器设备,获取过去24小时每个区域的客流量
const
allRegionIds
=
regionInfos
.
data
.
map
((
r
:
any
)
=>
r
.
id
);
let
totalRegionCustomerNum
=
0
;
// 所有区域的总客流量
let
regionMaxCustomerMap
=
new
Map
();
// {regionName: customerMaxNum}
// 建立 regionKey -> 显示名称 / maxCapacity 映射
let
regionCustomerMap
=
new
Map
();
// {regionName: customerNum}
const
regionKeyToName
=
new
Map
<
string
,
string
>
();
let
regionCustomerTable
=
[];
// [{regionName: A馆, customerNum: 5000, saturation: '80%'}]
const
regionMaxCustomerMap
=
new
Map
<
string
,
number
>
();
for
(
let
regionInfo
of
regionInfos
.
data
)
{
for
(
let
r
of
regionInfos
.
data
)
{
// 区分楼栋与楼层
let
name
=
regionKeys
.
length
===
0
?
r
.
groups
:
r
.
type
;
let
regionName
=
regionKeys
.
length
===
0
?
regionInfo
.
groups
:
regionInfo
.
type
;
regionKeyToName
.
set
(
r
.
id
,
name
);
// 如果是按区域类型统计,则最大承载量取该类型下所有区域的总和;如果是按区域名称统计,则取该区域的最大承载量
let
maxCap
=
regionKeys
.
length
===
0
?
regionMaxCustomerMap
.
get
(
name
)
:
r
.
max_capacity
;
let
maxCapacity
=
regionKeys
.
length
===
0
?
regionMaxCustomerMap
.
get
(
regionName
)
:
regionInfo
.
max_capacity
;
regionMaxCustomerMap
.
set
(
name
,
maxCap
);
regionMaxCustomerMap
.
set
(
regionName
,
maxCapacity
);
// 区域最大承载量
}
// 初始化客流量
let
totalNum
=
regionCustomerMap
.
get
(
regionName
)
?
regionCustomerMap
.
get
(
regionName
)
:
0
;
// 1. 批量查询所有客流传感器设备(1 次 DB 查询替代 N 次)
let
allCustomerDevices
=
await
selectDataListByParam
(
TABLENAME
.
设备表
,
{
let
customerDevices
=
await
selectDataListByParam
(
TABLENAME
.
设备表
,
{
device_type
:
"客流传感器"
,
device_type
:
"客流传感器"
,
region_key
:
regionInfo
.
id
region_key
:
{
"%in%"
:
allRegionIds
}
},
[
"device_type"
,
"device_id"
]);
},
[
"device_type"
,
"device_id"
,
"region_key"
]);
let
customerDeviceIds
=
customerDevices
.
data
.
map
((
d
:
any
)
=>
d
.
device_id
);
// 获取今日的客流量数据
const
deviceRegionMap
=
new
Map
<
string
,
string
>
();
if
(
customerDeviceIds
&&
customerDeviceIds
.
length
>
0
)
{
const
allCustomerDeviceIds
:
string
[]
=
[];
let
customerDeviceDatas
=
await
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
for
(
let
d
of
allCustomerDevices
.
data
)
{
device_id
:
{
"%in%"
:
customerDeviceIds
},
allCustomerDeviceIds
.
push
(
d
.
device_id
);
deviceRegionMap
.
set
(
d
.
device_id
,
d
.
region_key
);
}
let
regionCustomerMap
=
new
Map
<
string
,
number
>
();
let
totalRegionCustomerNum
=
0
;
let
todayCustomer
=
0
;
let
yesterdayCustomer
=
0
;
let
customerTrend
:
{
time
:
string
;
value
:
number
}[]
=
[];
let
regionCustomerTable
:
any
[]
=
[];
if
(
allCustomerDeviceIds
.
length
>
0
)
{
// 2. 批量查询今日设备数据(合并 per-region 统计 + 趋势图,1 次 DB 查询)
let
todayDatas
=
await
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
device_id
:
{
"%in%"
:
allCustomerDeviceIds
},
device_time
:
{
"%gte%"
:
formatLocalTime
(
todayStart
),
"%lte%"
:
formatLocalTime
(
todayEnd
)
},
device_time
:
{
"%gte%"
:
formatLocalTime
(
todayStart
),
"%lte%"
:
formatLocalTime
(
todayEnd
)
},
"%orderAsc%"
:
"device_time"
"%orderAsc%"
:
"device_time"
},
[
"device_id"
,
"data"
,
"device_time"
]);
},
[
"device_id"
,
"data"
,
"device_time"
]);
// 统计区域类型的客流量(新格式使用 in/out)
// 3. 批量查询昨日设备数据(1 次 DB 查询)
for
(
let
rec
of
customerDeviceDatas
.
data
)
{
let
yesterdayDatas
=
await
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
device_id
:
{
"%in%"
:
allCustomerDeviceIds
},
device_time
:
{
"%gte%"
:
formatLocalTime
(
yesterdayStart
),
"%lte%"
:
formatLocalTime
(
yesterdayEnd
)
},
"%orderAsc%"
:
"device_time"
},
[
"device_id"
,
"data"
,
"device_time"
]);
// 一次遍历今日数据:按区域统计 + 按小时聚合趋势
let
hourlyCustomerMap
=
new
Map
<
string
,
number
>
();
for
(
let
rec
of
todayDatas
.
data
)
{
let
data
=
rec
.
data
;
let
data
=
rec
.
data
;
// ====== 旧格式(已弃用,保留备用) ======
let
netVisitor
=
data
.
in
??
0
;
// if (data.current_total) {
// 区域统计(只统计 >0 的值)
// totalRegionCustomerNum += data.current_total;
// if (!regionCustomerMap.has(regionName)) {
// regionCustomerMap.set(regionName, data.current_total);
// } else {
// regionCustomerMap.set(regionName, totalNum + data.current_total);
// }
// }
const
netVisitor
=
(
data
.
in
??
0
);
if
(
netVisitor
>
0
)
{
if
(
netVisitor
>
0
)
{
totalRegionCustomerNum
+=
netVisitor
;
totalRegionCustomerNum
+=
netVisitor
;
if
(
!
regionCustomerMap
.
has
(
regionName
))
{
let
rk
=
deviceRegionMap
.
get
(
rec
.
device_id
)
||
''
;
regionCustomerMap
.
set
(
regionName
,
netVisitor
);
let
regionName
=
regionKeyToName
.
get
(
rk
)
||
rk
;
}
else
{
regionCustomerMap
.
set
(
regionName
,
(
regionCustomerMap
.
get
(
regionName
)
||
0
)
+
netVisitor
);
totalNum
=
regionCustomerMap
.
get
(
regionName
);
regionCustomerMap
.
set
(
regionName
,
totalNum
+
netVisitor
);
}
}
// 趋势聚合(所有值,含 0)
let
d
=
new
Date
(
rec
.
device_time
);
let
hourKey
=
`
${
d
.
getHours
()}
时`
;
hourlyCustomerMap
.
set
(
hourKey
,
(
hourlyCustomerMap
.
get
(
hourKey
)
||
0
)
+
netVisitor
);
}
}
// 昨日汇总
for
(
let
rec
of
yesterdayDatas
.
data
)
{
yesterdayCustomer
+=
(
rec
.
data
?.
in
??
0
);
}
}
}
else
{
regionCustomerMap
.
set
(
regionName
,
totalNum
);
// 补全无传感器区域为 0
for
(
let
r
of
regionInfos
.
data
)
{
let
name
=
regionKeyToName
.
get
(
r
.
id
)
||
''
;
if
(
!
regionCustomerMap
.
has
(
name
))
{
regionCustomerMap
.
set
(
name
,
0
);
}
}
}
}
// 当日客流
let
todayCustomer
=
0
;
let
yesterdayCustomer
=
0
;
// 构建区域饱和度表格
// 计算每个区域的饱和度(客流量/区域最大承载量),并转换成表格
for
(
let
[
regionName
,
customerNum
]
of
regionCustomerMap
.
entries
())
{
for
(
let
[
regionName
,
customerNum
]
of
regionCustomerMap
.
entries
())
{
let
regionMaxCustomer
=
regionMaxCustomerMap
.
get
(
regionName
)
||
1000
;
// 默认最大承载量为1000
let
maxCap
=
regionMaxCustomerMap
.
get
(
regionName
)
||
1000
;
let
saturation
=
`
${((
customerNum
/
regionMaxCustomer
)
*
100
)
>
100
?
100
:((
customerNum
/
regionMaxCustomer
)
*
100
).
toFixed
(
2
)}
%`
;
let
saturation
=
`
${
Math
.
min
(((
customerNum
/
maxCap
)
*
100
),
100
).
toFixed
(
2
)}
%`
;
regionCustomerTable
.
push
({
regionName
,
customerNum
,
saturation
});
regionCustomerTable
.
push
({
regionName
,
customerNum
,
saturation
});
todayCustomer
+=
customerNum
;
todayCustomer
+=
customerNum
;
}
}
// 6.3 客流量趋势分析:根据查询到的客流传感器数据,统计所选区域过去24小时客流量变化趋势
// 构建24小时趋势
let
customerTrend
=
[];
let
customerParams
:
any
=
{
device_type
:
"客流传感器"
};
if
(
regionGroup
)
{
customerParams
.
region_key
=
{
"%in%"
:
regionKeys
};
}
let
customerDevices
=
await
selectDataListByParam
(
TABLENAME
.
设备表
,
customerParams
,
[
"device_type"
,
"device_id"
]);
if
(
customerDevices
.
data
.
length
>
0
)
{
let
customerDeviceIds
=
customerDevices
.
data
.
map
((
d
:
any
)
=>
d
.
device_id
);
// 获取过去24小时客流量数据
let
customerDeviceDatas
=
await
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
device_id
:
{
"%in%"
:
customerDeviceIds
},
device_time
:
{
"%gte%"
:
formatLocalTime
(
todayStart
),
"%lte%"
:
formatLocalTime
(
todayEnd
)
},
"%orderAsc%"
:
"device_time"
},
[
"device_id"
,
"data"
,
"device_time"
]);
let
hourlyCustomerMap
=
new
Map
();
for
(
let
rec
of
customerDeviceDatas
.
data
)
{
let
d
=
new
Date
(
rec
.
device_time
);
let
hourKey
=
`
${
d
.
getHours
()}
时`
;
let
data
=
rec
.
data
;
// ====== 旧格式(已弃用,保留备用) ======
// let customerNum = data.current_total;
// ====== 新格式使用 in ======
let
customerNum
=
data
.
in
??
0
;
if
(
!
hourlyCustomerMap
.
has
(
hourKey
))
{
hourlyCustomerMap
.
set
(
hourKey
,
customerNum
);
}
else
{
let
totalNum
=
hourlyCustomerMap
.
get
(
hourKey
);
hourlyCustomerMap
.
set
(
hourKey
,
totalNum
+
customerNum
);
}
}
// 填充过去24小时的客流量趋势数据
for
(
let
i
=
0
;
i
<
24
;
i
++
)
{
for
(
let
i
=
0
;
i
<
24
;
i
++
)
{
let
key
=
`
${
i
}
时`
;
customerTrend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
hourlyCustomerMap
.
get
(
`
${
i
}
时`
)
??
0
});
let
val
=
hourlyCustomerMap
.
get
(
key
)
??
0
;
customerTrend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
val
});
}
}
}
else
{
// 获取昨日客流量数据
// 无客流传感器,所有区域填 0
let
yesterdayDatas
=
await
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
for
(
let
r
of
regionInfos
.
data
)
{
device_id
:
{
"%in%"
:
customerDeviceIds
},
let
name
=
regionKeyToName
.
get
(
r
.
id
)
||
''
;
device_time
:
{
"%gte%"
:
formatLocalTime
(
yesterdayStart
),
"%lte%"
:
formatLocalTime
(
yesterdayEnd
)
},
regionCustomerMap
.
set
(
name
,
0
);
"%orderAsc%"
:
"device_time"
regionCustomerTable
.
push
({
regionName
:
name
,
customerNum
:
0
,
saturation
:
'0%'
});
},
[
"device_id"
,
"data"
,
"device_time"
]);
for
(
let
rec
of
yesterdayDatas
.
data
)
{
let
data
=
rec
.
data
;
// ====== 旧格式(已弃用,保留备用) ======
// let customerNum = data.current_total;
// ====== 新格式使用 in ======
let
customerNum
=
data
.
in
??
0
;
yesterdayCustomer
+=
customerNum
;
}
}
}
}
// 6.4. 人流监控实时数据和趋势图数据
resultAny
.
customerOnlineTrend
=
{
resultAny
.
customerOnlineTrend
=
{
todayCustomer
:
todayCustomer
,
todayCustomer
:
todayCustomer
,
customerCompare
:
(
todayCustomer
-
yesterdayCustomer
)
/
yesterdayCustomer
*
100
,
// 同比昨日(今日人流量-昨日人流量)÷昨日人流量x100%
customerCompare
:
yesterdayCustomer
>
0
?
((
todayCustomer
-
yesterdayCustomer
)
/
yesterdayCustomer
*
100
)
:
0
,
passengerFlowTable
:
regionCustomerTable
,
passengerFlowTable
:
regionCustomerTable
,
customerTrend
:
customerTrend
customerTrend
:
customerTrend
};
};
return
{
todayCustomer
,
yesterdayCustomer
,
totalRegionCustomerNum
};
})(),
]);
const
energyResult
=
parallelResults
[
0
]
as
{
last24hRunningEnergy
:
number
};
const
customerResult
=
parallelResults
[
3
]
as
{
totalRegionCustomerNum
:
number
};
const
last24hRunningEnergy
=
energyResult
.
last24hRunningEnergy
;
const
totalRegionCustomerNum
=
customerResult
.
totalRegionCustomerNum
;
// 三级页面不需要执行此逻辑
// 三级页面不需要执行此逻辑
if
(
!
regionType
)
{
if
(
!
regionType
)
{
// 2.1 获取所选区域空调设备运行状态数据(如空调数、在线率、故障率等)
// AC + Fan + RegionList 彼此独立,并行执行
let
[
acResult
,
fanResult
,
regionMap
]
=
await
Promise
.
all
([
// 2. 空调设备运行监控
(
async
()
=>
{
let
acParams
:
any
=
{
device_type
:
"空调"
};
let
acParams
:
any
=
{
device_type
:
"空调"
};
if
(
regionGroup
)
{
if
(
regionGroup
)
{
acParams
.
region_key
=
{
"%in%"
:
regionKeys
};
acParams
.
region_key
=
{
"%in%"
:
regionKeys
};
...
@@ -585,6 +614,7 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -585,6 +614,7 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
let
acCount
=
acDeviceIds
.
length
;
let
acCount
=
acDeviceIds
.
length
;
let
acOfflineCount
=
acDevices
.
data
.
filter
((
d
:
any
)
=>
isDeviceOffline
(
d
.
device_state
??
0
)).
length
;
let
acOfflineCount
=
acDevices
.
data
.
filter
((
d
:
any
)
=>
isDeviceOffline
(
d
.
device_state
??
0
)).
length
;
let
acOnlineCount
=
acCount
-
acOfflineCount
;
let
acOnlineCount
=
acCount
-
acOfflineCount
;
let
acFaultRecords
=
await
selectDataListByParam
(
let
acFaultRecords
=
await
selectDataListByParam
(
TABLENAME
.
设备故障表
,
TABLENAME
.
设备故障表
,
{
status
:
{
"%ne%"
:
2
},
device_id
:
{
"%in%"
:
acDeviceIds
}
},
{
status
:
{
"%ne%"
:
2
},
device_id
:
{
"%in%"
:
acDeviceIds
}
},
...
@@ -592,55 +622,39 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -592,55 +622,39 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
);
);
let
acFaultDeviceIds
=
new
Set
(
acFaultRecords
.
data
.
map
((
r
:
any
)
=>
r
.
device_id
));
let
acFaultDeviceIds
=
new
Set
(
acFaultRecords
.
data
.
map
((
r
:
any
)
=>
r
.
device_id
));
let
acFaultDevices
=
acFaultDeviceIds
.
size
;
let
acFaultDevices
=
acFaultDeviceIds
.
size
;
let
acNormalRate
=
acCount
===
0
?
'100%'
:
(((
acCount
-
acFaultDevices
)
/
acCount
)
*
100
).
toFixed
(
2
)
+
'%'
;
let
acFaultCount
=
acFaultRecords
.
data
.
length
;
let
acFaultCount
=
acFaultRecords
.
data
.
length
;
//所有未解决的故障单数量(含同一设备多个故障)
// 2.2 获取今日空调在线趋势分析数据
let
acOnlineTrend
:
{
time
:
string
;
value
:
string
}[]
=
[];
let
acOnlineTrend
=
[];
if
(
acDeviceIds
.
length
)
{
if
(
acDeviceIds
.
length
)
{
// 2.2.1 获取过去24小时设备数据
let
last24h
=
new
Date
(
nowTime
.
getTime
()
-
24
*
3600000
);
let
last24h
=
new
Date
(
nowTime
.
getTime
()
-
24
*
3600000
);
let
acHistory
=
await
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
let
acHistory
=
await
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
device_id
:
{
"%in%"
:
acDeviceIds
},
device_id
:
{
"%in%"
:
acDeviceIds
},
device_time
:
{
"%gte%"
:
formatLocalTime
(
last24h
)
}
device_time
:
{
"%gte%"
:
formatLocalTime
(
last24h
)
}
},
[
"device_id"
,
"data"
,
"device_time"
]);
},
[
"device_id"
,
"data"
,
"device_time"
]);
// 2.2.2 按小时统计在线设备数和总设备数
let
hourlyData
=
new
Map
();
let
hourlyData
=
new
Map
();
for
(
let
rec
of
acHistory
.
data
)
{
for
(
let
rec
of
acHistory
.
data
)
{
let
d
=
new
Date
(
rec
.
device_time
);
let
d
=
new
Date
(
rec
.
device_time
);
let
hourKey
=
`
${
d
.
getHours
()}
时`
;
let
hourKey
=
`
${
d
.
getHours
()}
时`
;
let
data
=
rec
.
data
;
let
data
=
rec
.
data
;
// console.log(data);
if
(
!
hourlyData
.
has
(
hourKey
))
{
if
(
!
hourlyData
.
has
(
hourKey
))
{
let
totalNum
=
1
;
hourlyData
.
set
(
hourKey
,
{
online
:
data
.
power
===
"on"
?
1
:
0
,
total
:
1
});
let
onlineNum
=
data
.
power
===
"on"
?
1
:
0
;
hourlyData
.
set
(
hourKey
,
{
online
:
onlineNum
,
total
:
totalNum
});
}
else
{
}
else
{
let
totalNum
=
hourlyData
.
get
(
hourKey
).
total
;
let
v
=
hourlyData
.
get
(
hourKey
)
;
let
onlineNum
=
hourlyData
.
get
(
hourKey
).
online
;
if
(
data
.
power
===
"on"
)
v
.
online
+=
1
;
if
(
data
.
power
===
"on"
)
onlineNum
+=
1
;
v
.
total
+=
1
;
hourlyData
.
set
(
hourKey
,
{
online
:
onlineNum
,
total
:
totalNum
+
1
}
);
hourlyData
.
set
(
hourKey
,
v
);
}
}
}
}
console
.
log
(
"空调在线数据(过去24小时):"
,
hourlyData
.
size
);
// 2.2.3 填充24小时空调在线数据,没有在线数据的小时段则设为零,并计算每小时的空调在线率
for
(
let
i
=
0
;
i
<
24
;
i
++
)
{
for
(
let
i
=
0
;
i
<
24
;
i
++
)
{
let
key
=
`
${
i
}
时`
;
let
key
=
`
${
i
}
时`
;
let
val
=
hourlyData
.
get
(
key
);
let
val
=
hourlyData
.
get
(
key
);
acOnlineTrend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
val
?
((
val
.
online
/
val
.
total
)
*
100
).
toFixed
(
2
)
:
'0'
});
acOnlineTrend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
val
?
((
val
.
online
/
val
.
total
)
*
100
).
toFixed
(
2
)
:
'0'
});
}
}
}
}
// 2.4. 空调设备在线趋势图数据
return
{
acDeviceIds
,
acCount
,
acOnlineCount
,
acOfflineCount
,
acFaultCount
,
acOnlineTrend
};
resultAny
.
acDeviceOnlineTrend
=
{
})(),
acCount
:
acDeviceIds
.
length
,
// 3. 新风运行监控
acOnlineCount
:
acOnlineCount
,
(
async
()
=>
{
acOfflineCount
:
acOfflineCount
,
acFaultCount
:
acFaultCount
,
acOnlineTrend
:
acOnlineTrend
};
// 3.1 新风运行监控
let
fanParams
:
any
=
{
device_type
:
"新风"
};
let
fanParams
:
any
=
{
device_type
:
"新风"
};
if
(
regionGroup
)
{
if
(
regionGroup
)
{
fanParams
.
region_key
=
{
"%in%"
:
regionKeys
};
fanParams
.
region_key
=
{
"%in%"
:
regionKeys
};
...
@@ -651,26 +665,23 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -651,26 +665,23 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
let
fanCount
=
fanDeviceIds
.
length
;
let
fanCount
=
fanDeviceIds
.
length
;
let
fanOfflineCount
=
fanDevices
.
data
.
filter
((
d
:
any
)
=>
isDeviceOffline
(
d
.
device_state
??
0
)).
length
;
let
fanOfflineCount
=
fanDevices
.
data
.
filter
((
d
:
any
)
=>
isDeviceOffline
(
d
.
device_state
??
0
)).
length
;
let
fanOnlineCount
=
fanCount
-
fanOfflineCount
;
let
fanOnlineCount
=
fanCount
-
fanOfflineCount
;
let
fanFaultRecords
=
await
selectDataListByParam
(
let
fanFaultRecords
=
await
selectDataListByParam
(
TABLENAME
.
设备故障表
,
TABLENAME
.
设备故障表
,
{
status
:
{
"%ne%"
:
2
},
device_id
:
{
"%in%"
:
fanDeviceIds
}
},
{
status
:
{
"%ne%"
:
2
},
device_id
:
{
"%in%"
:
fanDeviceIds
}
},
[
"device_id"
]
[
"device_id"
]
);
);
let
fanFaultDeviceIds
=
new
Set
(
fanFaultRecords
.
data
.
map
((
r
:
any
)
=>
r
.
device_id
));
let
fanFaultDeviceIds
=
new
Set
(
fanFaultRecords
.
data
.
map
((
r
:
any
)
=>
r
.
device_id
));
let
fanFaultDevices
=
fanFaultDeviceIds
.
size
;
let
fanFaultCount
=
fanFaultRecords
.
data
.
length
;
let
fanNormalRate
=
fanCount
===
0
?
'100%'
:
(((
fanCount
-
fanFaultDevices
)
/
fanCount
)
*
100
).
toFixed
(
2
)
+
'%'
;
let
fanFaultCount
=
fanFaultRecords
.
data
.
length
;
//所有未解决的故障单数量(含同一设备多个故障)
// 3.2 新风风量运行监测趋势分析数据(如果有风量数据)
let
fanOnlineTrend
:
{
time
:
string
;
value
:
string
}[]
=
[];
let
fanOnlineTrend
=
[];
if
(
fanDeviceIds
.
length
)
{
if
(
fanDeviceIds
.
length
)
{
let
fanDeviceDatas
=
await
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
let
fanDeviceDatas
=
await
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
device_id
:
{
"%in%"
:
fanDeviceIds
},
device_id
:
{
"%in%"
:
fanDeviceIds
},
device_time
:
{
"%gte%"
:
formatLocalTime
(
last24hStart
),
"%lte%"
:
formatLocalTime
(
nowTime
)
},
device_time
:
{
"%gte%"
:
formatLocalTime
(
last24hStart
),
"%lte%"
:
formatLocalTime
(
nowTime
)
},
"%orderAsc%"
:
"device_time"
"%orderAsc%"
:
"device_time"
},
[
"device_id"
,
"data"
,
"device_time"
]);
},
[
"device_id"
,
"data"
,
"device_time"
]);
// 3.2.1 按小时统计新风送风量(换气次数=送风量/房间体积)
let
hourlyData
=
new
Map
();
let
hourlyData
=
new
Map
();
// {key: 00:00, ly: 0, ry: 0 }
for
(
let
rec
of
fanDeviceDatas
.
data
)
{
for
(
let
rec
of
fanDeviceDatas
.
data
)
{
let
d
=
new
Date
(
rec
.
device_time
);
let
d
=
new
Date
(
rec
.
device_time
);
let
hourKey
=
`
${
d
.
getHours
()}
时`
;
let
hourKey
=
`
${
d
.
getHours
()}
时`
;
...
@@ -679,36 +690,46 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
...
@@ -679,36 +690,46 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
hourlyData
.
set
(
hourKey
,
{
airSupply
:
airSupplyKeyMap
[
data
.
fanSpeed
]
});
hourlyData
.
set
(
hourKey
,
{
airSupply
:
airSupplyKeyMap
[
data
.
fanSpeed
]
});
}
}
}
}
// 3.2.2 填充24小时新风监控数据,没有在线数据的小时段则设为零,并计算每小时的新风送风量
for
(
let
i
=
0
;
i
<
24
;
i
++
)
{
for
(
let
i
=
0
;
i
<
24
;
i
++
)
{
let
key
=
`
${
i
}
时`
;
let
key
=
`
${
i
}
时`
;
let
val
=
hourlyData
.
get
(
key
);
let
val
=
hourlyData
.
get
(
key
);
fanOnlineTrend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
val
?
val
.
airSupply
:
'0'
});
fanOnlineTrend
.
push
({
time
:
`
${
i
}
:00`
,
value
:
val
?
val
.
airSupply
:
'0'
});
}
}
}
}
return
{
fanDeviceIds
,
fanCount
,
fanOnlineCount
,
fanOfflineCount
,
fanFaultCount
,
fanOnlineTrend
};
})(),
// 7. 区域列表
getRegionList
(
regionGroup
)
]);
// 2.4. 空调设备在线趋势图数据
resultAny
.
acDeviceOnlineTrend
=
{
acCount
:
acResult
.
acCount
,
acOnlineCount
:
acResult
.
acOnlineCount
,
acOfflineCount
:
acResult
.
acOfflineCount
,
acFaultCount
:
acResult
.
acFaultCount
,
acOnlineTrend
:
acResult
.
acOnlineTrend
};
// 3.3. 新风设备在线趋势图数据
// 3.3. 新风设备在线趋势图数据
resultAny
.
fanDeviceOnlineTrend
=
{
resultAny
.
fanDeviceOnlineTrend
=
{
fanCount
:
fan
DeviceIds
.
length
,
fanCount
:
fan
Result
.
fanCount
,
fanOnlineCount
:
fanOnlineCount
,
fanOnlineCount
:
fan
Result
.
fan
OnlineCount
,
fanOfflineCount
:
fanOfflineCount
,
fanOfflineCount
:
fan
Result
.
fan
OfflineCount
,
fanFaultCount
:
fanFaultCount
,
fanFaultCount
:
fan
Result
.
fan
FaultCount
,
fanOnlineTrend
:
fanOnlineTrend
fanOnlineTrend
:
fan
Result
.
fan
OnlineTrend
};
};
// 7.1 获取区域列表接口,供前端下拉选择区域类型和区域名称
let
regionMap
:
any
=
await
getRegionList
(
regionGroup
);
// 7.2. 区域列表数据
// 7.2. 区域列表数据
resultAny
.
regionData
=
regionMap
;
resultAny
.
regionData
=
regionMap
;
// 运行监控总数据(一天)
// 运行监控总数据(一天)
resultAny
.
mainRunningManagement
=
{
resultAny
.
mainRunningManagement
=
{
totalCustomerNumber
:
totalRegionCustomerNum
,
totalCustomerNumber
:
totalRegionCustomerNum
,
totalDeviceOnlineRate
:
(
acOnlineCount
/
acCount
*
100
)
>=
100
?
'100'
:(
acOnlineCount
/
acCount
*
100
).
toFixed
(
1
),
totalDeviceOnlineRate
:
(
acResult
.
acOnlineCount
/
acResult
.
acCount
*
100
)
>=
100
?
'100'
:
(
acResult
.
acOnlineCount
/
acResult
.
acCount
*
100
).
toFixed
(
1
),
// totalElectricity: last24hRunningEnergy.toFixed(0).slice(0, 5),
totalElectricity
:
last24hRunningEnergy
.
toFixed
(
0
),
totalElectricity
:
last24hRunningEnergy
.
toFixed
(
0
),
};
};
}
else
{
}
else
{
// 设备监控总数据
let
devices
=
await
selectDataListByParam
(
TABLENAME
.
设备表
,
{
let
devices
=
await
selectDataListByParam
(
TABLENAME
.
设备表
,
{
region_key
:
{
"%in%"
:
regionKeys
}
region_key
:
{
"%in%"
:
regionKeys
}
},
[
"device_id"
,
"device_state"
]);
},
[
"device_id"
,
"device_state"
]);
...
@@ -902,46 +923,6 @@ export async function controlAcRunning(params: {}) {
...
@@ -902,46 +923,6 @@ export async function controlAcRunning(params: {}) {
return
result
;
return
result
;
}
}
// 辅助函数:获取用电趋势(按间隔分组)
async
function
getEnergyTrend
(
deviceIds
:
string
[],
startTime
:
Date
,
endTime
:
Date
,
granularity
:
'hour'
|
'day'
|
'month'
)
{
if
(
deviceIds
.
length
===
0
)
return
[];
let
records
=
await
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
device_id
:
{
"%in%"
:
deviceIds
},
device_time
:
{
"%gte%"
:
formatLocalTime
(
startTime
),
"%lte%"
:
formatLocalTime
(
endTime
)
},
"%orderAsc%"
:
"device_time"
},
[
"device_id"
,
"data"
,
"device_time"
]);
let
intervalMap
=
new
Map
();
// key: 时间字符串, value: { deviceMin, deviceMax }
for
(
let
rec
of
records
.
data
)
{
let
data
=
rec
.
data
;
// let energy = data.energy ?? data.total_energy;
let
energy
=
data
.
current
;
if
(
energy
===
undefined
)
continue
;
let
intervalKey
:
string
;
let
d
=
new
Date
(
rec
.
device_time
);
const
pad
=
(
n
:
number
)
=>
String
(
n
).
padStart
(
2
,
'0'
);
if
(
granularity
===
'hour'
)
{
intervalKey
=
`
${
d
.
getFullYear
()}
-
${
pad
(
d
.
getMonth
()
+
1
)}
-
${
pad
(
d
.
getDate
())}
${
pad
(
d
.
getHours
())}
时`
;
}
else
if
(
granularity
===
'day'
)
{
intervalKey
=
`
${
d
.
getFullYear
()}
/
${
pad
(
d
.
getMonth
()
+
1
)}
/
${
pad
(
d
.
getDate
())}
`
;
}
else
{
intervalKey
=
`
${
d
.
getFullYear
()}
-
${
pad
(
d
.
getMonth
()
+
1
)}
`
;
}
if
(
!
intervalMap
.
has
(
intervalKey
))
{
intervalMap
.
set
(
intervalKey
,
energy
);
}
else
{
energy
=
intervalMap
.
get
(
intervalKey
)
+
energy
;
intervalMap
.
set
(
intervalKey
,
energy
);
}
}
let
result
=
[];
for
(
let
[
key
,
energy
]
of
intervalMap
.
entries
())
{
result
.
push
({
key
,
value
:
energy
.
toFixed
(
0
)
});
}
result
.
sort
((
a
,
b
)
=>
a
.
key
.
localeCompare
(
b
.
key
));
return
result
;
}
/**
/**
* 获取本月累计用电(按天)
* 获取本月累计用电(按天)
* 查询三相电表/电能监测设备的每日用电量,补全当月 1 日到今天的 x 轴
* 查询三相电表/电能监测设备的每日用电量,补全当月 1 日到今天的 x 轴
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment