数据集成-电表关联区域;运行分析客流监控和空气质量优化

parent ae21dd9b
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
"tencentcloud-sdk-nodejs": "^4.0.562", "tencentcloud-sdk-nodejs": "^4.0.562",
"winston": "^3.17.0", "winston": "^3.17.0",
"ws": "^5.2.2", "ws": "^5.2.2",
"xlsx": "^0.18.5",
"xml2js": "^0.4.23", "xml2js": "^0.4.23",
"xmlrpc": "^1.3.2" "xmlrpc": "^1.3.2"
}, },
...@@ -292,16 +293,10 @@ ...@@ -292,16 +293,10 @@
} }
}, },
"node_modules/adler-32": { "node_modules/adler-32": {
"version": "1.2.0", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.2.0.tgz", "resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz",
"integrity": "sha1-aj5r8KY5ALoVZSgIyxXGgT0aXyU=", "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
"dependencies": { "license": "Apache-2.0",
"exit-on-epipe": "~1.0.1",
"printj": "~1.1.0"
},
"bin": {
"adler32": "bin/adler32.njs"
},
"engines": { "engines": {
"node": ">=0.8" "node": ">=0.8"
} }
...@@ -637,13 +632,13 @@ ...@@ -637,13 +632,13 @@
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
}, },
"node_modules/cfb": { "node_modules/cfb": {
"version": "1.2.0", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.0.tgz", "resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz",
"integrity": "sha512-sXMvHsKCICVR3Naq+J556K+ExBo9n50iKl6LGarlnvuA2035uMlGA/qVrc0wQtow5P1vJEw9UyrKLCbtIKz+TQ==", "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
"license": "Apache-2.0",
"dependencies": { "dependencies": {
"adler-32": "~1.2.0", "adler-32": "~1.3.0",
"crc-32": "~1.2.0", "crc-32": "~1.2.0"
"printj": "~1.1.2"
}, },
"engines": { "engines": {
"node": ">=0.8" "node": ">=0.8"
...@@ -658,25 +653,14 @@ ...@@ -658,25 +653,14 @@
} }
}, },
"node_modules/codepage": { "node_modules/codepage": {
"version": "1.14.0", "version": "1.15.0",
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.14.0.tgz", "resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz",
"integrity": "sha1-jL4lSBMjVZ19MHVxsP/5HnodL5k=", "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
"dependencies": { "license": "Apache-2.0",
"commander": "~2.14.1",
"exit-on-epipe": "~1.0.1"
},
"bin": {
"codepage": "bin/codepage.njs"
},
"engines": { "engines": {
"node": ">=0.8" "node": ">=0.8"
} }
}, },
"node_modules/codepage/node_modules/commander": {
"version": "2.14.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz",
"integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw=="
},
"node_modules/color": { "node_modules/color": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz", "resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz",
...@@ -730,8 +714,9 @@ ...@@ -730,8 +714,9 @@
}, },
"node_modules/commander": { "node_modules/commander": {
"version": "2.17.1", "version": "2.17.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", "resolved": "https://registry.npmmirror.com/commander/-/commander-2.17.1.tgz",
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
"license": "MIT"
}, },
"node_modules/compressible": { "node_modules/compressible": {
"version": "2.0.18", "version": "2.0.18",
...@@ -847,13 +832,10 @@ ...@@ -847,13 +832,10 @@
} }
}, },
"node_modules/crc-32": { "node_modules/crc-32": {
"version": "1.2.0", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz", "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==", "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
"dependencies": { "license": "Apache-2.0",
"exit-on-epipe": "~1.0.1",
"printj": "~1.1.0"
},
"bin": { "bin": {
"crc32": "bin/crc32.njs" "crc32": "bin/crc32.njs"
}, },
...@@ -1010,6 +992,7 @@ ...@@ -1010,6 +992,7 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmmirror.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz", "resolved": "https://registry.npmmirror.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
"integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==", "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==",
"license": "Apache-2.0",
"engines": { "engines": {
"node": ">=0.8" "node": ">=0.8"
} }
...@@ -1200,8 +1183,9 @@ ...@@ -1200,8 +1183,9 @@
}, },
"node_modules/fflate": { "node_modules/fflate": {
"version": "0.3.11", "version": "0.3.11",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.3.11.tgz", "resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.3.11.tgz",
"integrity": "sha512-Rr5QlUeGN1mbOHlaqcSYMKVpPbgLy0AWT/W0EHxA6NGI12yO1jpoui2zBBvU2G824ltM6Ut8BFgfHSBGfkmS0A==" "integrity": "sha512-Rr5QlUeGN1mbOHlaqcSYMKVpPbgLy0AWT/W0EHxA6NGI12yO1jpoui2zBBvU2G824ltM6Ut8BFgfHSBGfkmS0A==",
"license": "MIT"
}, },
"node_modules/finalhandler": { "node_modules/finalhandler": {
"version": "1.3.1", "version": "1.3.1",
...@@ -2172,6 +2156,68 @@ ...@@ -2172,6 +2156,68 @@
"node": ">=4.0.0" "node": ">=4.0.0"
} }
}, },
"node_modules/node-xlsx/node_modules/adler-32": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.2.0.tgz",
"integrity": "sha512-/vUqU/UY4MVeFsg+SsK6c+/05RZXIHZMGJA+PX5JyWI0ZRcBpupnRuPLU/NXXoFwMYCPCoxIfElM2eS+DUXCqQ==",
"license": "Apache-2.0",
"dependencies": {
"exit-on-epipe": "~1.0.1",
"printj": "~1.1.0"
},
"bin": {
"adler32": "bin/adler32.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/node-xlsx/node_modules/codepage": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.14.0.tgz",
"integrity": "sha512-iz3zJLhlrg37/gYRWgEPkaFTtzmnEv1h+r7NgZum2lFElYQPi0/5bnmuDfODHxfp0INEfnRqyfyeIJDbb7ahRw==",
"license": "Apache-2.0",
"dependencies": {
"commander": "~2.14.1",
"exit-on-epipe": "~1.0.1"
},
"bin": {
"codepage": "bin/codepage.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/node-xlsx/node_modules/codepage/node_modules/commander": {
"version": "2.14.1",
"resolved": "https://registry.npmmirror.com/commander/-/commander-2.14.1.tgz",
"integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw==",
"license": "MIT"
},
"node_modules/node-xlsx/node_modules/xlsx": {
"version": "0.16.9",
"resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.16.9.tgz",
"integrity": "sha512-gxi1I3EasYvgCX1vN9pGyq920Ron4NO8PNfhuoA3Hpq6Y8f0ECXiy4OLrK4QZBnj1jx3QD+8Fq5YZ/3mPZ5iXw==",
"license": "Apache-2.0",
"dependencies": {
"adler-32": "~1.2.0",
"cfb": "^1.1.4",
"codepage": "~1.14.0",
"commander": "~2.17.1",
"crc-32": "~1.2.0",
"exit-on-epipe": "~1.0.1",
"fflate": "^0.3.8",
"ssf": "~0.11.2",
"wmf": "~1.0.1",
"word": "~0.3.0"
},
"bin": {
"xlsx": "bin/xlsx.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/nodemailer": { "node_modules/nodemailer": {
"version": "6.5.0", "version": "6.5.0",
"resolved": "https://registry.npm.taobao.org/nodemailer/download/nodemailer-6.5.0.tgz?cache=0&sync_timestamp=1614329161993&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnodemailer%2Fdownload%2Fnodemailer-6.5.0.tgz", "resolved": "https://registry.npm.taobao.org/nodemailer/download/nodemailer-6.5.0.tgz?cache=0&sync_timestamp=1614329161993&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnodemailer%2Fdownload%2Fnodemailer-6.5.0.tgz",
...@@ -2261,6 +2307,7 @@ ...@@ -2261,6 +2307,7 @@
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmmirror.com/printj/-/printj-1.1.2.tgz", "resolved": "https://registry.npmmirror.com/printj/-/printj-1.1.2.tgz",
"integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==", "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==",
"license": "Apache-2.0",
"bin": { "bin": {
"printj": "bin/printj.njs" "printj": "bin/printj.njs"
}, },
...@@ -3290,17 +3337,15 @@ ...@@ -3290,17 +3337,15 @@
} }
}, },
"node_modules/xlsx": { "node_modules/xlsx": {
"version": "0.16.9", "version": "0.18.5",
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.16.9.tgz", "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz",
"integrity": "sha512-gxi1I3EasYvgCX1vN9pGyq920Ron4NO8PNfhuoA3Hpq6Y8f0ECXiy4OLrK4QZBnj1jx3QD+8Fq5YZ/3mPZ5iXw==", "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
"dependencies": { "license": "Apache-2.0",
"adler-32": "~1.2.0", "dependencies": {
"cfb": "^1.1.4", "adler-32": "~1.3.0",
"codepage": "~1.14.0", "cfb": "~1.2.1",
"commander": "~2.17.1", "codepage": "~1.15.0",
"crc-32": "~1.2.0", "crc-32": "~1.2.1",
"exit-on-epipe": "~1.0.1",
"fflate": "^0.3.8",
"ssf": "~0.11.2", "ssf": "~0.11.2",
"wmf": "~1.0.1", "wmf": "~1.0.1",
"word": "~0.3.0" "word": "~0.3.0"
...@@ -3604,13 +3649,9 @@ ...@@ -3604,13 +3649,9 @@
} }
}, },
"adler-32": { "adler-32": {
"version": "1.2.0", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.2.0.tgz", "resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz",
"integrity": "sha1-aj5r8KY5ALoVZSgIyxXGgT0aXyU=", "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A=="
"requires": {
"exit-on-epipe": "~1.0.1",
"printj": "~1.1.0"
}
}, },
"agent-base": { "agent-base": {
"version": "6.0.2", "version": "6.0.2",
...@@ -3860,13 +3901,12 @@ ...@@ -3860,13 +3901,12 @@
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
}, },
"cfb": { "cfb": {
"version": "1.2.0", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.0.tgz", "resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz",
"integrity": "sha512-sXMvHsKCICVR3Naq+J556K+ExBo9n50iKl6LGarlnvuA2035uMlGA/qVrc0wQtow5P1vJEw9UyrKLCbtIKz+TQ==", "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
"requires": { "requires": {
"adler-32": "~1.2.0", "adler-32": "~1.3.0",
"crc-32": "~1.2.0", "crc-32": "~1.2.0"
"printj": "~1.1.2"
} }
}, },
"charenc": { "charenc": {
...@@ -3875,20 +3915,9 @@ ...@@ -3875,20 +3915,9 @@
"integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
}, },
"codepage": { "codepage": {
"version": "1.14.0", "version": "1.15.0",
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.14.0.tgz", "resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz",
"integrity": "sha1-jL4lSBMjVZ19MHVxsP/5HnodL5k=", "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA=="
"requires": {
"commander": "~2.14.1",
"exit-on-epipe": "~1.0.1"
},
"dependencies": {
"commander": {
"version": "2.14.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz",
"integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw=="
}
}
}, },
"color": { "color": {
"version": "3.2.1", "version": "3.2.1",
...@@ -3940,7 +3969,7 @@ ...@@ -3940,7 +3969,7 @@
}, },
"commander": { "commander": {
"version": "2.17.1", "version": "2.17.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", "resolved": "https://registry.npmmirror.com/commander/-/commander-2.17.1.tgz",
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
}, },
"compressible": { "compressible": {
...@@ -4021,13 +4050,9 @@ ...@@ -4021,13 +4050,9 @@
} }
}, },
"crc-32": { "crc-32": {
"version": "1.2.0", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz", "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==", "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="
"requires": {
"exit-on-epipe": "~1.0.1",
"printj": "~1.1.0"
}
}, },
"crypt": { "crypt": {
"version": "0.0.2", "version": "0.0.2",
...@@ -4276,7 +4301,7 @@ ...@@ -4276,7 +4301,7 @@
}, },
"fflate": { "fflate": {
"version": "0.3.11", "version": "0.3.11",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.3.11.tgz", "resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.3.11.tgz",
"integrity": "sha512-Rr5QlUeGN1mbOHlaqcSYMKVpPbgLy0AWT/W0EHxA6NGI12yO1jpoui2zBBvU2G824ltM6Ut8BFgfHSBGfkmS0A==" "integrity": "sha512-Rr5QlUeGN1mbOHlaqcSYMKVpPbgLy0AWT/W0EHxA6NGI12yO1jpoui2zBBvU2G824ltM6Ut8BFgfHSBGfkmS0A=="
}, },
"finalhandler": { "finalhandler": {
...@@ -5002,6 +5027,50 @@ ...@@ -5002,6 +5027,50 @@
"@babel/runtime": "^7.12.5", "@babel/runtime": "^7.12.5",
"buffer-from": "^1.1.1", "buffer-from": "^1.1.1",
"xlsx": "^0.16.9" "xlsx": "^0.16.9"
},
"dependencies": {
"adler-32": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.2.0.tgz",
"integrity": "sha512-/vUqU/UY4MVeFsg+SsK6c+/05RZXIHZMGJA+PX5JyWI0ZRcBpupnRuPLU/NXXoFwMYCPCoxIfElM2eS+DUXCqQ==",
"requires": {
"exit-on-epipe": "~1.0.1",
"printj": "~1.1.0"
}
},
"codepage": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.14.0.tgz",
"integrity": "sha512-iz3zJLhlrg37/gYRWgEPkaFTtzmnEv1h+r7NgZum2lFElYQPi0/5bnmuDfODHxfp0INEfnRqyfyeIJDbb7ahRw==",
"requires": {
"commander": "~2.14.1",
"exit-on-epipe": "~1.0.1"
},
"dependencies": {
"commander": {
"version": "2.14.1",
"resolved": "https://registry.npmmirror.com/commander/-/commander-2.14.1.tgz",
"integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw=="
}
}
},
"xlsx": {
"version": "0.16.9",
"resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.16.9.tgz",
"integrity": "sha512-gxi1I3EasYvgCX1vN9pGyq920Ron4NO8PNfhuoA3Hpq6Y8f0ECXiy4OLrK4QZBnj1jx3QD+8Fq5YZ/3mPZ5iXw==",
"requires": {
"adler-32": "~1.2.0",
"cfb": "^1.1.4",
"codepage": "~1.14.0",
"commander": "~2.17.1",
"crc-32": "~1.2.0",
"exit-on-epipe": "~1.0.1",
"fflate": "^0.3.8",
"ssf": "~0.11.2",
"wmf": "~1.0.1",
"word": "~0.3.0"
}
}
} }
}, },
"nodemailer": { "nodemailer": {
...@@ -5832,17 +5901,14 @@ ...@@ -5832,17 +5901,14 @@
} }
}, },
"xlsx": { "xlsx": {
"version": "0.16.9", "version": "0.18.5",
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.16.9.tgz", "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz",
"integrity": "sha512-gxi1I3EasYvgCX1vN9pGyq920Ron4NO8PNfhuoA3Hpq6Y8f0ECXiy4OLrK4QZBnj1jx3QD+8Fq5YZ/3mPZ5iXw==", "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
"requires": { "requires": {
"adler-32": "~1.2.0", "adler-32": "~1.3.0",
"cfb": "^1.1.4", "cfb": "~1.2.1",
"codepage": "~1.14.0", "codepage": "~1.15.0",
"commander": "~2.17.1", "crc-32": "~1.2.1",
"crc-32": "~1.2.0",
"exit-on-epipe": "~1.0.1",
"fflate": "^0.3.8",
"ssf": "~0.11.2", "ssf": "~0.11.2",
"wmf": "~1.0.1", "wmf": "~1.0.1",
"word": "~0.3.0" "word": "~0.3.0"
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
"tencentcloud-sdk-nodejs": "^4.0.562", "tencentcloud-sdk-nodejs": "^4.0.562",
"winston": "^3.17.0", "winston": "^3.17.0",
"ws": "^5.2.2", "ws": "^5.2.2",
"xlsx": "^0.18.5",
"xml2js": "^0.4.23", "xml2js": "^0.4.23",
"xmlrpc": "^1.3.2" "xmlrpc": "^1.3.2"
}, },
......
...@@ -7,6 +7,8 @@ import { ...@@ -7,6 +7,8 @@ import {
} from "./feiyiClient"; } from "./feiyiClient";
import { mysqlModelMap } from "../model/sqlModelBind"; import { mysqlModelMap } from "../model/sqlModelBind";
import moment from "moment"; import moment from "moment";
import XLSX from "xlsx";
import path from "path";
// ==================== 工具函数 ==================== // ==================== 工具函数 ====================
...@@ -17,6 +19,42 @@ function getTodayDateStr(): string { ...@@ -17,6 +19,42 @@ function getTodayDateStr(): string {
return moment().format('YYYYMMDD'); return moment().format('YYYYMMDD');
} }
/**
* 从内机四段地址(如 "2-1-0-1")提取外机地址("2-1-0")
*/
function getOuterAddress(indoorUnitAddressFull: string): string | null {
if (!indoorUnitAddressFull) return null;
const parts = indoorUnitAddressFull.split('-');
if (parts.length >= 3) {
return parts.slice(0, 3).join('-');
}
return null;
}
/**
* 从 Excel 文件读取电表设备编号 → 外机地址 的映射表
*/
function loadMeterOuterAddrMap(): Map<string, string> {
const map = new Map<string, string>();
try {
const filePath = path.resolve(__dirname, '../../res/电表对应区域.xlsx');
const workbook = XLSX.readFile(filePath);
const sheet = workbook.Sheets[workbook.SheetNames[0]];
const rows: any[] = XLSX.utils.sheet_to_json(sheet);
for (const row of rows) {
const meterCode = row['电表设备编号'];
const outerAddr = row['空调外机地址'];
if (meterCode && outerAddr) {
map.set(String(meterCode), String(outerAddr));
}
}
console.log(`[工具] 电表对应区域.xlsx 读取完成,共 ${map.size} 条映射`);
} catch (err) {
console.error('[工具] 读取电表对应区域.xlsx 失败:', err);
}
return map;
}
// ==================== 分页拉取辅助函数 ==================== // ==================== 分页拉取辅助函数 ====================
/** /**
...@@ -150,11 +188,137 @@ export async function region() { ...@@ -150,11 +188,137 @@ export async function region() {
} }
console.log(`[区域集成] 完成,新增 ${insertCount} 条,跳过 ${skipCount} 条已存在`); console.log(`[区域集成] 完成,新增 ${insertCount} 条,跳过 ${skipCount} 条已存在`);
// 2. 一次性查询所有 region,后续两步共用内存数据,避免 DB 读写时序问题
const allRegions = await regionModel.findAll({ raw: true }) as any[];
// 3. 同步空调内机 → 反写 region.address
const updatedRegions = await syncRegionAddressByAcDevices(regionModel, allRegions);
// 4. 同步电表设备 region_key(复用上一步已更新 address 的内存数据)
await syncMeterDeviceRegionKey(updatedRegions);
} catch (err) { } catch (err) {
console.error('[区域集成] 执行异常:', err); console.error('[区域集成] 执行异常:', err);
} }
} }
// ==================== 区域-设备关联同步 ====================
/**
* 通过空调内机信息反写 region 表的 address 字段(外机地址)
* 逻辑:拉取所有空调内机 → 通过 roomId 匹配 region → 用内机地址前三位反写 region.address
*/
async function syncRegionAddressByAcDevices(regionModel: any, allRegions: any[]) {
console.log('[同步region.address] 开始...');
try {
// 拉取所有空调内机
const rows = await fetchAllPages((page, limit) => getIndoorUnitList({ page, limit }));
console.log(`[同步region.address] 共获取 ${rows.length} 条空调内机`);
// 构建 roomId → region 映射(使用外部传入的 allRegions)
const roomIdToRegion = new Map<string, any>();
for (const r of allRegions) {
if (r.room_id) {
roomIdToRegion.set(r.room_id, r);
}
}
let updateCount = 0;
for (const item of rows) {
if (!item.roomId || !item.indoorUnitAddressFull) continue;
const region = roomIdToRegion.get(item.roomId);
if (!region) continue;
// 用内机四段地址前三位作为外机地址
const outerAddr = getOuterAddress(item.indoorUnitAddressFull);
if (!outerAddr) continue;
// address 为空或值不同时才更新
if (region.address === outerAddr) continue;
await regionModel.update(
{ address: outerAddr },
{ where: { id: region.id } },
);
// 同步更新内存中的值,后续 syncMeterDeviceRegionKey 可直接使用
region.address = outerAddr;
updateCount++;
}
console.log(`[同步region.address] 完成,更新 ${updateCount} 条`);
return allRegions; // 返回已更新 address 的内存数据
} catch (err) {
console.error('[同步region.address] 执行异常:', err);
return allRegions; // 异常时仍返回原始数据,不阻塞后续流程
}
}
/**
* 通过 Excel 映射 + region.address 同步电表设备的 region_key
* 逻辑:Excel(电表设备编号→外机地址) → region.address → region.id → 更新 device.region_key
*/
async function syncMeterDeviceRegionKey(allRegions: any[]) {
console.log('[同步电表region_key] 开始...');
try {
const deviceModel = mysqlModelMap['device'];
if (!deviceModel) {
console.error('[同步电表region_key] device 表模型未初始化,跳过');
return;
}
// 1. 读取 Excel 映射:电表设备编号 → 外机地址
const meterOuterMap = loadMeterOuterAddrMap();
if (meterOuterMap.size === 0) {
console.warn('[同步电表region_key] Excel 映射为空,跳过');
return;
}
// 2. 使用外部传入的 allRegions 构建 address → regionId 映射(复用已更新 address 的内存数据)
const addrToRegionId = new Map<string, number>();
for (const r of allRegions) {
if (r.address) {
addrToRegionId.set(r.address, r.id);
}
}
console.log(`[同步电表region_key] region.address 映射共 ${addrToRegionId.size} 条`);
// 3. 查询所有电表设备
const meterDevices = await deviceModel.findAll({
where: { device_type: '电能监测' },
raw: true,
});
console.log(`[同步电表region_key] 共 ${meterDevices.length} 个电表设备`);
// 4. 逐一匹配更新
let updateCount = 0;
let missCount = 0;
for (const device of meterDevices as any[]) {
const outerAddr = meterOuterMap.get(device.device_id);
if (!outerAddr) {
missCount++;
continue;
}
const regionId = addrToRegionId.get(outerAddr);
if (!regionId) {
missCount++;
continue;
}
// region_key 已正确则跳过
if (device.region_key === regionId) continue;
await deviceModel.update(
{ region_key: regionId },
{ where: { id: device.id } },
);
updateCount++;
}
console.log(`[同步电表region_key] 完成,更新 ${updateCount} 条,未匹配 ${missCount} 条`);
} catch (err) {
console.error('[同步电表region_key] 执行异常:', err);
}
}
// ==================== 设备集成 ==================== // ==================== 设备集成 ====================
/** 空调控制参数模板(按设备数据结构.md) */ /** 空调控制参数模板(按设备数据结构.md) */
......
...@@ -128,13 +128,17 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) { ...@@ -128,13 +128,17 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
let airQualityDevices = await selectDataListByParam(TABLENAME.设备表, airParams, ["device_id"]); let airQualityDevices = await selectDataListByParam(TABLENAME.设备表, airParams, ["device_id"]);
let airDeviceIds = airQualityDevices.data.map(d => d.device_id); let airDeviceIds = airQualityDevices.data.map(d => d.device_id);
let qualityIndex = 300; let qualityIndex = 300;
// 今日环境质量
let todayEnvironmental = '优秀';
let currentCo2 = 0; // 当前二氧化氮
let co2Trend = []; let co2Trend = [];
let currentCo2 = '0ppm'; let currentTemperature = '0℃'; // 当前温度
let currentTemperature = '0℃';
let temperatureTrend = []; let temperatureTrend = [];
let currentHumidity = '0%'; let currentHumidity = '0%'; // 当前湿度
let humidityTrend = []; let humidityTrend = [];
let currentPm25 = '0μg/m³'; let currentPm25 = 0; // 当前PM2.5
let pm25Trend = []; let pm25Trend = [];
let co2TrendDetail = []; let co2TrendDetail = [];
...@@ -149,8 +153,8 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) { ...@@ -149,8 +153,8 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
let airData = latestAir.data[0].data; let airData = latestAir.data[0].data;
currentTemperature = `${airData.temperature ?? 0}`; currentTemperature = `${airData.temperature ?? 0}`;
currentHumidity = `${airData.humidity ?? 0}%`; currentHumidity = `${airData.humidity ?? 0}%`;
currentPm25 = `${airData.pm25 ?? 0}μg/m³`; currentPm25 = airData.pm25;
currentCo2 = `${airData.co2 ?? 0}ppm`; currentCo2 = airData.co2 ?? 0;
qualityIndex = 500 - (airData.pm25 ?? 0); qualityIndex = 500 - (airData.pm25 ?? 0);
} }
// 过去24小时趋势 // 过去24小时趋势
...@@ -179,12 +183,27 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) { ...@@ -179,12 +183,27 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
co2Trend.push({ time: `${i}时`, value: co2Val.toString() }); co2Trend.push({ time: `${i}时`, value: co2Val.toString() });
} }
} }
// 计算今日环境质量
if (currentCo2 && currentPm25) {
// 优秀:CO2< 800ppm; PM2.5≤35; 甲醛≤0.055
todayEnvironmental = currentCo2<800 && currentPm25<=35 ? '优秀':
// 优秀:CO2< 1000ppm; PM2.5≤45; TVOC≤0.4
currentCo2<1000 && currentPm25<=45 ? '优秀':
// 良好:CO2≤ 1000ppm; PM2.5≤50; 甲醛≤0.08
currentCo2<=1000 && currentPm25<=50 ? '良好':
// 一般:CO2:1000~1500; PM2.5:50~75
currentCo2>1000 && currentCo2<1500 && currentPm25>50 && currentPm25<75 ? '一般': '差'
}
// 4.2. 环境监测趋势图数据 // 4.2. 环境监测趋势图数据
resultAny.environmentalTrend = { resultAny.environmentalTrend = {
todayEnvironmental: todayEnvironmental,
currentTemperature: currentTemperature, currentTemperature: currentTemperature,
currentHumidity: currentHumidity, currentHumidity: currentHumidity,
currentPm25: currentPm25, currentPm25: currentPm25+'μg/m³',
currentCo2: currentCo2, currentCo2: currentCo2+'ppm',
temperatureTrend: temperatureTrend, temperatureTrend: temperatureTrend,
humidityTrend: humidityTrend, humidityTrend: humidityTrend,
pm25Trend: pm25Trend, pm25Trend: pm25Trend,
...@@ -234,10 +253,10 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) { ...@@ -234,10 +253,10 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
let regionInfos: any = []; let regionInfos: any = [];
if (regionKeys.length === 0) { if (regionKeys.length === 0) {
// 6.1.1 若没有选择区域,则查询区域表获取所有区域类型 // 6.1.1 若没有选择区域,则查询区域表获取所有区域类型
regionInfos = await selectDataListByParam(TABLENAME.区域表, {}, ["id", "name", "type"]); regionInfos = await selectDataListByParam(TABLENAME.区域表, {}, ["id", "room_id", "name", "type", "groups"]);
} else { } else {
// 6.1.2 若选择了区域,则查询选择的区域 // 6.1.2 若选择了区域,则查询选择的区域
regionInfos = await selectDataListByParam(TABLENAME.区域表, { id: { "%in%": regionKeys } }, ["id", "name", "type"]); regionInfos = await selectDataListByParam(TABLENAME.区域表, { id: { "%in%": regionKeys } }, ["id", "room_id", "name", "type", "groups"]);
} }
// 6.2 循环区域查询客流传感器设备,获取过去24小时每个区域的客流量 // 6.2 循环区域查询客流传感器设备,获取过去24小时每个区域的客流量
...@@ -246,36 +265,40 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) { ...@@ -246,36 +265,40 @@ export async function getRunAnalysis(regionGroup: string, regionType: string) {
let regionCustomerMap = new Map(); // {regionName: customerNum} let regionCustomerMap = new Map(); // {regionName: customerNum}
let regionCustomerTable = []; // [{regionName: A馆, customerNum: 5000, saturation: '80%'}] let regionCustomerTable = []; // [{regionName: A馆, customerNum: 5000, saturation: '80%'}]
for (let regionInfo of regionInfos.data) { for (let regionInfo of regionInfos.data) {
let customerDevices = await selectDataListByParam(TABLENAME.设备表, { // 区分楼栋与楼层
device_type: "客流传感器", region_key: regionInfo.id let regionName = regionKeys.length === 0 ? regionInfo.groups : regionInfo.type;
// 如果是按区域类型统计,则最大承载量取该类型下所有区域的总和;如果是按区域名称统计,则取该区域的最大承载量
let maxCapacity = regionKeys.length === 0 ? regionMaxCustomerMap.get(regionName) : regionInfo.max_capacity;
regionMaxCustomerMap.set(regionName, maxCapacity); // 区域最大承载量
// 初始化客流量
let totalNum = regionCustomerMap.get(regionName) ? regionCustomerMap.get(regionName) : 0;
let customerDevices = await selectDataListByParam(TABLENAME.设备表, {
device_type: "客流传感器", region_key: regionInfo.id
}, ["device_type", "device_id"]); }, ["device_type", "device_id"]);
let customerDeviceIds = customerDevices.data.map((d: any) => d.device_id); let customerDeviceIds = customerDevices.data.map((d: any) => d.device_id);
// 获取今日的客流量数据 // 获取今日的客流量数据
if (customerDeviceIds) { if (customerDeviceIds && customerDeviceIds.length > 0) {
let customerDeviceDatas = await selectDataListByParam(TABLENAME.设备数据表, { let customerDeviceDatas = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": customerDeviceIds }, device_id: { "%in%": customerDeviceIds },
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"]);
// 统计区域类型的客流量 // 统计区域类型的客流量
for (let rec of customerDeviceDatas.data) { for (let rec of customerDeviceDatas.data) {
let data = rec.data; let data = rec.data;
if (data.presence === '进') { if (data.presence === '进') {
totalRegionCustomerNum += 1; totalRegionCustomerNum += 1;
// 区分楼栋与楼层
let regionName = regionKeys.length === 0 ? regionInfo.type : regionInfo.name;
if (!regionCustomerMap.has(regionName)) { if (!regionCustomerMap.has(regionName)) {
regionCustomerMap.set(regionName, 1); regionCustomerMap.set(regionName, 1);
// 如果是按区域类型统计,则最大承载量取该类型下所有区域的总和;如果是按区域名称统计,则取该区域的最大承载量
let maxCapacity = regionKeys.length === 0 ? regionMaxCustomerMap.get(regionName) : regionInfo.max_capacity;
regionMaxCustomerMap.set(regionName, maxCapacity); // 区域最大承载量
} else { } else {
let totalNum = regionCustomerMap.get(regionName);
regionCustomerMap.set(regionName, totalNum + 1); regionCustomerMap.set(regionName, totalNum + 1);
} }
} }
} }
} else {
regionCustomerMap.set(regionName, totalNum);
} }
} }
// 计算每个区域的饱和度(客流量/区域最大承载量),并转换成表格 // 计算每个区域的饱和度(客流量/区域最大承载量),并转换成表格
......
...@@ -257,6 +257,10 @@ export const TablesConfig = [ ...@@ -257,6 +257,10 @@ export const TablesConfig = [
defaultValue: 0, defaultValue: 0,
comment: '排序序号' comment: '排序序号'
}, },
address: {
type: DataTypes.STRING(100),
comment: '外机地址'
},
created_at: { created_at: {
type: DataTypes.DATE, type: DataTypes.DATE,
allowNull: false, allowNull: false,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment