From b016b3effeea7322c50d9f4f4c0bb7db53875626 Mon Sep 17 00:00:00 2001 From: CODE <1650192445@qq.com> Date: Fri, 25 Aug 2023 17:53:13 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E5=9C=A8=E7=BA=BF=E7=8E=87?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/.vscode/launch.json | 1 + api/app/lib/controllers/project/group.js | 152 ++++++++++++++++++++--- api/app/lib/models/structure_off.js | 62 +++++++++ api/app/lib/routes/project/index.js | 10 +- api/config.js | 8 +- api/sequelize-automate.config.js | 4 +- 6 files changed, 211 insertions(+), 26 deletions(-) create mode 100644 api/app/lib/models/structure_off.js diff --git a/api/.vscode/launch.json b/api/.vscode/launch.json index 4b7bd60..7b45a9f 100644 --- a/api/.vscode/launch.json +++ b/api/.vscode/launch.json @@ -22,6 +22,7 @@ // "-g postgres://FashionAdmin:123456@10.8.30.156:5432/POMS", "-k 10.8.30.72:29092,10.8.30.73:29092,10.8.30.74:29092", "-e http://10.8.30.60:9200", + // "-e http://10.8.30.60:5601", "--iotaProxy http://10.8.30.157:17007", "--redisHost localhost", "--redisPort 6379", diff --git a/api/app/lib/controllers/project/group.js b/api/app/lib/controllers/project/group.js index a41cf3c..150da65 100644 --- a/api/app/lib/controllers/project/group.js +++ b/api/app/lib/controllers/project/group.js @@ -103,7 +103,7 @@ async function delGroup (ctx) { } } -async function groupStatic (ctx) { +async function groupStatistic (ctx) { try { const { models } = ctx.fs.dc; const { userId } = ctx.fs.api @@ -114,16 +114,11 @@ async function groupStatic (ctx) { where: { pomsUserId: userId } - }) // 获取全部的 poms 项目id 并构建关系 let pomsProjectIds = new Set() - let groupMap = { - - } for (let group of progectGroupList) { - groupMap[group.id] = groupMap[group.id] || {} for (let projectId of group.pomsProjectIds) { pomsProjectIds.add(projectId) } @@ -142,33 +137,151 @@ async function groupStatic (ctx) { for (let projectId of project.anxinProjectId) { anxinProjectIds.add(projectId) } - groupMap[group.id].anxinProjectCount = anxinProjectCount } let anxinProjectIdArr = Array.from(anxinProjectIds) - // 统计安心云项目下的结构物个数 - const strucCountRes = await clickHouse.anxinyun.query( + // 统计安心云项目下的结构物id + const strucIdRes = await clickHouse.anxinyun.query( ` - SELECT project, COUNT(*) AS count + SELECT * FROM t_project_structure WHERE project IN (${[...anxinProjectIdArr].join(',')}, -1) - GROUP BY project ` ).toPromise() + let strucIds = new Set() + for (let struc of strucIdRes) { + strucIds.add(struc.structure) + } + + let strucIdArr = Array.from(strucIds) + const deviceOffTimeRes = strucIdArr.length ? await models.StructureOff.findAll({ + where: { + structure: { $in: strucIdArr } + } + }) : [] + let rslt = [] for (let pg of progectGroupList) { - let anxinProjectCount = 0 - for (let projectId of pg.pomsProjectIds) { - if (anxinProjectIdArr.includes(projectId)) { - anxinProjectCount++ - } - } let strucCount = 0 + let maxOffLineTime = 0 + let anxinProjectCount = groupProjectRes.reduce((count, gp) => { + if (pg.pomsProjectIds.some(id => gp.id == id)) { + // 如果 pg 分组信息 存储的 pomsProjectIds 运维项目id 和 真正的 groupProjectRes 运维项目id 相等 + // gp.anxinProjectId 就是对应的 安心云的项目 id 集 + count += gp.anxinProjectId.length + // + let strucIdArr_ = + strucIdRes.filter(s => gp.anxinProjectId.includes(s.project)) + strucCount += strucIdArr_.length + // + for (let { dataValues: off } of deviceOffTimeRes) { + if (strucIdArr_.some((s) => s.structure == off.structure)) { + if (off.offline > maxOffLineTime) { + maxOffLineTime = off.offline + } + } + } + } + return count + }, 0) + rslt.unshift({ + ...pg.dataValues, + strucCount, + maxOffLineTime, + anxinProjectCount, + }) } ctx.status = 200; - ctx.body = [] + ctx.body = rslt + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: error`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +async function groupStatisticOnline (ctx) { + try { + const { models } = ctx.fs.dc; + const { userId } = ctx.fs.api + const { groupId } = ctx.query + const sequelize = ctx.fs.dc.orm + const { clickHouse } = ctx.app.fs + + const pomsProjectRes = await sequelize.query(` + SELECT project_correlation.anxin_project_id + FROM project_group + JOIN project_correlation + ON project_correlation.id = ANY(project_group.poms_project_ids) + WHERE project_group.id = ${groupId}; + `) + + const anxinProjectIds = new Set() + for (let pomsProject of (pomsProjectRes[0] || [])) { + for (let pid of pomsProject.anxin_project_id) + anxinProjectIds.add(pid) + } + + const strucIdRes = await clickHouse.anxinyun.query( + ` + SELECT * + FROM t_project_structure + WHERE project IN (${[...anxinProjectIds].join(',')}, -1) + ` + ).toPromise() + let strucIds = new Set() + for (let struc of strucIdRes) { + strucIds.add(struc.structure) + } + let strucIdArr = Array.from(strucIds) + + const strucRes = strucIdArr.length ? await clickHouse.anxinyun.query( + ` + SELECT * FROM t_structure WHERE id IN (${[...strucIdArr].join(',')}); + ` + ).toPromise() : [] + + const strucOnlineClient = ctx.app.fs.esclient.strucOnline + const onlineRes = await strucOnlineClient.search({ + index: strucOnlineClient.config.index, + type: strucOnlineClient.config.type, + body: { + "query": { + "bool": { + "filter": [ + { + "range": { + "collect_time": { + "gte": `${moment().subtract(24, 'hours').format('YYYY-MM-DDTHH:mm:ss.SSS')}Z`, + "gte": "2023-08-24T08:00:00.000Z", + } + } + }, + { + "terms": { + "structure": strucIdArr, + "structure": [1, 2, 3] + } + } + ] + } + }, + "sort": [ + { + "collect_time": { + "order": "asc" + } + } + ] + } + }) + + ctx.status = 200; + ctx.body = onlineRes.hits.hits; } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: error`); ctx.status = 400; @@ -182,5 +295,6 @@ module.exports = { groupList, editGroup, delGroup, - groupStatic, + groupStatistic, + groupStatisticOnline, }; \ No newline at end of file diff --git a/api/app/lib/models/structure_off.js b/api/app/lib/models/structure_off.js new file mode 100644 index 0000000..e286832 --- /dev/null +++ b/api/app/lib/models/structure_off.js @@ -0,0 +1,62 @@ +/* eslint-disable*/ + +'use strict'; + +module.exports = dc => { + const DataTypes = dc.ORM; + const sequelize = dc.orm; + const StructureOff = sequelize.define("structureOff", { + id: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: true, + field: "id", + autoIncrement: true, + unique: "t_structure_off_id_uindex" + }, + structure: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: "结构物id", + primaryKey: false, + field: "structure", + autoIncrement: false + }, + offline: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: "离线时长(分钟)", + primaryKey: false, + field: "offline", + autoIncrement: false + }, + offtime: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: null, + comment: "最后数据时间", + primaryKey: false, + field: "offtime", + autoIncrement: false + }, + state: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: "数据状态( 0:有效,1:无效)", + primaryKey: false, + field: "state", + autoIncrement: false + } + }, { + tableName: "t_structure_off", + comment: "", + indexes: [] + }); + dc.models.StructureOff = StructureOff; + return StructureOff; +}; \ No newline at end of file diff --git a/api/app/lib/routes/project/index.js b/api/app/lib/routes/project/index.js index fe11906..4cb4c3a 100644 --- a/api/app/lib/routes/project/index.js +++ b/api/app/lib/routes/project/index.js @@ -40,6 +40,12 @@ module.exports = function (app, router, opts) { app.fs.api.logAttr['DEL/project/group'] = { content: '删除项目分组', visible: true }; router.delete('/project/group', projectGroup.delGroup); - app.fs.api.logAttr['GET/project/group/static'] = { content: '获取项目分组统计信息', visible: true }; - router.get('/project/group/static', projectGroup.groupStatic); + app.fs.api.logAttr['GET/project/group/statistic'] = { content: '获取项目分组统计信息', visible: true }; + router.get('/project/group/statistic', projectGroup.groupStatistic); + + app.fs.api.logAttr['GET/project/group/statistic'] = { content: '获取项目分组统计信息', visible: true }; + router.get('/project/group/statistic', projectGroup.groupStatistic); + + app.fs.api.logAttr['GET/project/group/statistic/online'] = { content: '获取项目分组在线率统计信息', visible: true }; + router.get('/project/group/statistic/online', projectGroup.groupStatisticOnline); }; \ No newline at end of file diff --git a/api/config.js b/api/config.js index b8da0ee..c535356 100644 --- a/api/config.js +++ b/api/config.js @@ -124,7 +124,9 @@ const CAIYUN_API = process.env.CAIYUN_API || flags.caiyunApi || 'https://api.cai const CAIYUN_KEY = process.env.CAIYUN_KEY || flags.caiyunKey || '1l0eNveMANMXEIJI'; // ES -const ES_PLATFORM_NAME = process.env.ES_PLATFORM_NAME || flags.esPlatformName || 'anxinyun'; +const ES_PLATFORM_NAME = process.env.ES_PLATFORM_NAME || flags.esPlatformName + || 'anxincloud' + || 'anxinyun'; const ANXINCLOUD_ES_NODES_REST = process.env.ANXINCLOUD_ES_NODES_REST || flags.es; if ( @@ -277,10 +279,10 @@ const product = { ] }, es: { - alarm: { + strucOnline: { //告警记录 rootURL: ANXINCLOUD_ES_NODES_REST.split(','), - index: `${ES_PLATFORM_NAME}_alarms`, + index: `${ES_PLATFORM_NAME}_online`, type: flags.esType ? flags.esType : '_doc' }, } diff --git a/api/sequelize-automate.config.js b/api/sequelize-automate.config.js index a4dd7af..0f2730c 100644 --- a/api/sequelize-automate.config.js +++ b/api/sequelize-automate.config.js @@ -33,10 +33,10 @@ module.exports = { dir: './app/lib/models', // 指定输出 models 文件的目录 typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义 emptyDir: false, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir` - tables: ['project_group'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 + tables: ['t_structure_off'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性 tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中 - ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面 + ignorePrefix: ['t_'], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面 attrLength: false, // 在生成模型的字段中 是否生成 如 var(128)这种格式,公司一般使用 String ,则配置为 false }, } \ No newline at end of file