From 705b0a12f9050e0621eca5e09377e99ed0fbabee Mon Sep 17 00:00:00 2001 From: "gao.zhiyuan" Date: Wed, 24 Aug 2022 15:42:20 +0800 Subject: [PATCH] mirror api 60% --- .../api/app/lib/controllers/camera/index.js | 32 +- .../api/app/lib/controllers/mirror/index.js | 507 ++++++++++++++++++ code/VideoAccess-VCMP/api/app/lib/index.js | 3 + .../api/app/lib/models/mirror.js | 9 + .../api/app/lib/models/mirror_camera.js | 17 +- .../api/app/lib/routes/mirror/index.js | 23 + .../api/sequelize-automate.config.js | 2 +- 7 files changed, 585 insertions(+), 8 deletions(-) create mode 100644 code/VideoAccess-VCMP/api/app/lib/controllers/mirror/index.js create mode 100644 code/VideoAccess-VCMP/api/app/lib/routes/mirror/index.js diff --git a/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js b/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js index 030806f..b763778 100644 --- a/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js @@ -43,7 +43,7 @@ async function getCameraProject (ctx, next) { } let gbCameraOption = { model: models.GbCamera, - attributes: ['id', 'online', 'playUrl','did'], + attributes: ['id', 'online', 'playUrl', 'did'], required: false } if (limit) { @@ -180,10 +180,11 @@ async function getCamera (ctx) { model: models.Vender }, { model: models.SecretYingshi, - attributes: ['token'] + attributes: ['token'], + required: false }, { model: models.GbCamera, - attributes: ['id', 'online', 'playUrl',], + attributes: ['id', 'online', 'playUrl', 'did'], required: false }, { model: models.CameraRemark, @@ -263,11 +264,26 @@ async function getCameraListAll (ctx) { const { models } = ctx.fs.dc; const cameraRes = await models.Camera.findAll({ - attributes: ['id', 'name', 'type'], + attributes: { exclude: ['delete', 'recycleTime', 'rtmp', 'createUserId', 'nvrId', 'kindId', 'yingshiSecretId', 'gbId'] }, where: { delete: false, recycleTime: null, - } + }, + include: [{ + model: models.SecretYingshi, + attributes: ['token'], + required: false + }, { + model: models.GbCamera, + attributes: ['id', 'online', 'playUrl', 'did'], + required: false + }, { + model: models.CameraRemark, + attributes: ['remark'], + order: [ + ['id', 'DESC'] + ], + }] }) ctx.status = 200; @@ -310,6 +326,12 @@ async function del (ctx) { const { cameraId } = ctx.params const { token } = ctx.fs.api + await models.MirrorCamera.destroy({ + where: { + cameraId: cameraId + }, + transaction + }) await models.CameraStatusPushMonitor.destroy({ where: { cameraId: cameraId diff --git a/code/VideoAccess-VCMP/api/app/lib/controllers/mirror/index.js b/code/VideoAccess-VCMP/api/app/lib/controllers/mirror/index.js new file mode 100644 index 0000000..6756398 --- /dev/null +++ b/code/VideoAccess-VCMP/api/app/lib/controllers/mirror/index.js @@ -0,0 +1,507 @@ +'use strict'; +const moment = require('moment') + +async function edit (ctx) { + const transaction = await ctx.fs.dc.orm.transaction(); + try { + const sequelize = ctx.fs.dc.orm; + const { models } = ctx.fs.dc; + const { userId } = ctx.fs.api + const data = ctx.request.body + const timeNow = moment() + const { mirrorId, tree = [], filterGroup = [] } = data + let mirrorId_ = mirrorId + + if (mirrorId_) { + let upData = { + template: data.template, + updateTime: timeNow.format(), + title: data.title, + showHeader: Boolean(data.showHeader), + } + if (data.publish) { + upData.publish = true + upData.publishTime = timeNow.format() + } + await models.Mirror.update(upData, { + where: { + id: mirrorId_ + }, + transaction + }) + + // 除 Mirror 外的信息全删除并重建 + await models.MirrorCamera.destroy({ + where: { + mirrorId: mirrorId_ + }, + transaction + }) + await sequelize.query('DELETE FROM mirror_filter WHERE group_id IN (SELECT id FROM mirror_filter_group WHERE mirror_id=?)', { + replacements: [mirrorId_], + transaction + }) + await models.MirrorFilterGroup.destroy({ + where: { + mirrorId: mirrorId_ + }, + transaction + }) + await models.MirrorTree.destroy({ + where: { + mirrorId: mirrorId_ + }, + transaction + }) + } else { + // 创建 镜像信息 + let createData = { + template: data.template, + createUser: userId, + createTime: timeNow.format(), + title: data.title, + showHeader: Boolean(data.showHeader), + publish: false, + mid: timeNow.format(`ssmmHHDDMMYYYY`) + } + if (data.publish) { + createData.publish = true + createData.publishTime = timeNow.format() + } + const mirrorCreateRes = await models.Mirror.create(createData, { + transaction + }) + mirrorId_ = mirrorCreateRes.id + } + + let cameraStorage = {} + const dealTree = async (child, lastNodeStorageId, level) => { + // 递归 tree 获得扁平信息 + let curLevel = level + for (let c of child) { + if (c.cameraId) { + // 摄像头节点为末端节点 + // 不创建节点 + if (cameraStorage[c.cameraId]) { + cameraStorage[c.cameraId].treeIds.push(lastNodeStorageId) + } else { + cameraStorage[c.cameraId] = { + treeIds: [lastNodeStorageId], + filterIds: [] + } + } + } else { + const treeStorageRes = await models.MirrorTree.create({ + name: c.label, + level: curLevel, + dependence: lastNodeStorageId, + mirrorId: mirrorId_ + }, { + transaction + }) + + if (c.children) { + await dealTree(c.children, treeStorageRes.id, curLevel + 1) + } + } + } + } + await dealTree(tree, null, 1) + + for (let g of filterGroup) { + // 遍历创建筛选分组 + const filterGroupStorageRes = await models.MirrorFilterGroup.create({ + name: g.name, + forbidden: g.forbidden, + mirrorId: mirrorId_ + }, { + transaction + }) + if (g.filters) { + for (let f of g.filters) { + // 遍历创建筛选项 + const filterStorageRes = await models.MirrorFilter.create({ + name: f.name, + groupId: filterGroupStorageRes.id + }, { + transaction + }) + for (let cid of f.cameraIds) { + if (cameraStorage[cid]) { + cameraStorage[cid].filterIds.push(filterStorageRes.id) + } + } + } + } + } + + let bulkCreateCameraD = [] + for (let cid in cameraStorage) { + bulkCreateCameraD.push({ + cameraId: cid, + ...cameraStorage[cid], + mirrorId: mirrorId_ + }) + } + if (bulkCreateCameraD.length) { + await models.MirrorCamera.bulkCreate(bulkCreateCameraD, { + transaction + }) + } + + await transaction.commit(); + ctx.status = 204; + } catch (error) { + await transaction.rollback(); + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +async function list (ctx) { + try { + const { models } = ctx.fs.dc; + const { userId, token } = ctx.fs.api + + const mirrorRes = await models.Mirror.findAll({ + attributes: { + exclude: ['showHeader'] + }, + where: {}, + order: [['id', 'DESC']] + }) + let createUserIds = new Set() + + for (let c of mirrorRes) { + createUserIds.add(c.dataValues.createUser) + } + + // 查对应创建者信息 + const corUsers = await ctx.app.fs.authRequest.get(`user/${[...createUserIds].join(',') || -1}/message`, { query: { token } }) + for (let { dataValues: mirror } of mirrorRes) { + const corUser = corUsers.find(u => u.id == mirror.createUser) + mirror.createUser = corUser ? corUser.username : '' + } + + ctx.status = 200; + ctx.body = mirrorRes + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +async function get (ctx) { + try { + // 下次这样的关关联联要用数据库关联 !!! + // 不然逻辑好绕啊 + const { models } = ctx.fs.dc; + const { userId } = ctx.fs.api + const { mid } = ctx.params + // 查当前镜像的全部信息 + const mirrorRes = await models.Mirror.findOne({ + attributes: { + exclude: ['createUser'] + }, + order: [['id', 'ASC']], + where: { + mid: mid + }, + include: [{ + model: models.MirrorTree, + required: false + }, { + model: models.MirrorFilterGroup, + attributes: { + exclude: ['mirrorId'] + }, + required: false, + include: [{ + model: models.MirrorFilter, + attributes: { + exclude: ['groupId'] + }, + required: false + }] + }, { + model: models.MirrorCamera, + required: false, + include: { + model: models.Camera, + attributes: { + exclude: ['rtmp', 'venderId', 'createTime', 'recycleTime', 'delete', 'createUserId', 'nvrId', 'kindId', 'yingshiSecretId', 'gbId'] + }, + include: [{ + model: models.SecretYingshi, + attributes: ['token'] + }, { + model: models.GbCamera, + attributes: ['id', 'online', 'playUrl',], + required: false + }, { + model: models.CameraRemark, + attributes: ['remark'], + order: [ + ['id', 'DESC'] + ], + }] + } + }] + }) + + let returnData + if (mirrorRes) { + const { mirrorCameras, mirrorFilterGroups, mirrorTrees } = mirrorRes.dataValues + returnData = { + ...mirrorRes.dataValues, + tree: [], + filterGroup: [] + } + + // 反向构建出树节点 + const buildTree = (treeNodes = [], lastLevelKey = '') => { + const nextKeyPre = lastLevelKey ? lastLevelKey + '-' : lastLevelKey + treeNodes.forEach((tn, index) => { + const curKey = nextKeyPre + index + let child = JSON.parse(JSON.stringify(mirrorTrees.filter(mt => mt.dependence == tn.id))) + let cameras = mirrorCameras.filter(mc => mc.treeIds.includes(tn.id)) + if (cameras) { + // 有摄像头 向下再创建一级节点 + tn.children = cameras.map((c, cIndex) => { + return { + label: c.dataValues.camera.name, + key: curKey + '-' + cIndex, + cameraId: c.dataValues.cameraId, + camera: c.dataValues.camera + } + }) + } else { + tn.children = child + buildTree(child, curKey) + } + tn.label = tn.name + tn.key = curKey + + delete tn.name + delete tn.level + delete tn.dependence + delete tn.mirrorId + }) + } + let tree = JSON.parse(JSON.stringify(mirrorTrees.filter(t => t.level == 1))) + buildTree(tree) + returnData.tree = tree + + // 构建筛选分组及筛选项 + for (let { dataValues: g } of mirrorFilterGroups) { + for (let { dataValues: f } of g.mirrorFilters) { + f.cameraIds = (mirrorCameras.filter(mc => mc.filterIds.includes(f.id)) || []).map(mc => mc.cameraId) + } + g.filters = g.mirrorFilters + delete g.mirrorFilters + } + returnData.filterGroup = mirrorFilterGroups + + delete returnData.mirrorCameras + delete returnData.mirrorFilterGroups + delete returnData.mirrorTrees + } else { + throw '没有查询到对应的镜像服务' + } + + ctx.status = 200; + ctx.body = returnData + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +async function del (ctx) { + const transaction = await ctx.fs.dc.orm.transaction(); + try { + const { models } = ctx.fs.dc; + const { userId } = ctx.fs.api + const { mirrorId } = ctx.params + + if (!mirrorId) throw '镜像服务删除失败'; + + const sequelize = ctx.fs.dc.orm; + // 除 Mirror 外的信息全删除并重建 + await models.MirrorCamera.destroy({ + where: { + mirrorId: mirrorId + }, + transaction + }) + await sequelize.query('DELETE FROM mirror_filter WHERE group_id IN (SELECT id FROM mirror_filter_group WHERE mirror_id=?)', { + replacements: [mirrorId], + transaction + }) + await models.MirrorFilterGroup.destroy({ + where: { + mirrorId: mirrorId + }, + transaction + }) + await models.MirrorTree.destroy({ + where: { + mirrorId: mirrorId + }, + transaction + }) + await models.Mirror.destroy({ + where: { + id: mirrorId + }, + transaction + }) + + await transaction.commit(); + ctx.status = 204; + } catch (error) { + await transaction.rollback(); + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +async function publish (ctx) { + try { + const { models } = ctx.fs.dc; + const { userId } = ctx.fs.api + const { mirrorId } = ctx.params + + if (!mirrorId) throw '发布镜像服务失败'; + + await models.Mirror.update({ + publish: true, + publishTime: moment().format() + }, { + where: { + id: mirrorId + } + }) + + ctx.status = 204; + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +async function copy (ctx) { + const transaction = await ctx.fs.dc.orm.transaction(); + try { + const { models } = ctx.fs.dc; + const { userId } = ctx.fs.api + const { mirrorId } = ctx.params + const timeNow = moment() + // 查当前镜像的全部信息 + const mirrorRes = await models.Mirror.findOne({ + attributes: { + exclude: ['createUser'] + }, + order: [['id', 'ASC']], + where: { + id: mirrorId + }, + include: [{ + model: models.MirrorTree, + required: false + }, { + model: models.MirrorFilterGroup, + required: false, + include: [{ + model: models.MirrorFilter, + required: false + }] + }, { + model: models.MirrorCamera, + required: false, + }] + }) + + if (mirrorRes) { + const { mirrorCameras, mirrorFilterGroups, mirrorTrees } = mirrorRes.dataValues + + const newMirrorRes = await models.Mirror.create({ + template: mirrorRes.template, + createUser: userId, + createTime: timeNow.format(), + title: data.title, + showHeader: mirrorRes.showHeader, + publish: mirrorRes.publish, + mid: timeNow.format(`ssmmHHDDMMYYYY`) + }, { + transaction + }) + + const newMirrorId = newMirrorRes.id + let cameraStorage = {} + let treeLevelOne = mirrorTrees.filter(mt => mt.level == 1) + + const storageTree = (node) => { + for (let n of node) { + + } + } + storageTree(treeLevelOne) + + for (let { dataValues: mt } of mirrorTrees) { + const newTreeNodeStorageRes = await models.MirrorTree.create({ + name: c.label, + level: curLevel, + dependence: lastNodeStorageId, + mirrorId: mirrorId_ + }) + let corCamera = mirrorCameras.filter((mc) => { mc.treeIds.includes(mt.id) }) + for (let c of corCamera) { + if (cameraStorage[c.cameraId]) { + cameraStorage[c.cameraId].treeIds.push(lastNodeStorageId) + } else { + cameraStorage[c.cameraId] = { + treeIds: [lastNodeStorageId], + filterIds: [] + } + } + } + } + + } + + await transaction.commit(); + ctx.status = 204; + } catch (error) { + await transaction.rollback(); + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +module.exports = { + edit, + list, + get, + del, + publish, + copy, +}; \ No newline at end of file diff --git a/code/VideoAccess-VCMP/api/app/lib/index.js b/code/VideoAccess-VCMP/api/app/lib/index.js index 03fe1a3..5937553 100644 --- a/code/VideoAccess-VCMP/api/app/lib/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/index.js @@ -120,4 +120,7 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq MirrorCamera.belongsTo(Camera, { foreignKey: 'cameraId', targetKey: 'id' }); Camera.hasMany(MirrorCamera, { foreignKey: 'cameraId', sourceKey: 'id' }); + + MirrorCamera.belongsTo(Mirror, { foreignKey: 'mirrorId', targetKey: 'id' }); + Mirror.hasMany(MirrorCamera, { foreignKey: 'mirrorId', sourceKey: 'id' }); }; diff --git a/code/VideoAccess-VCMP/api/app/lib/models/mirror.js b/code/VideoAccess-VCMP/api/app/lib/models/mirror.js index 3655770..a15e004 100644 --- a/code/VideoAccess-VCMP/api/app/lib/models/mirror.js +++ b/code/VideoAccess-VCMP/api/app/lib/models/mirror.js @@ -86,6 +86,15 @@ module.exports = dc => { primaryKey: false, field: "mid", autoIncrement: false + }, + publishTime: { + type: DataTypes.DATE, + allowNull: true, + defaultValue: null, + comment: null, + primaryKey: false, + field: "publish_time", + autoIncrement: false } }, { tableName: "mirror", diff --git a/code/VideoAccess-VCMP/api/app/lib/models/mirror_camera.js b/code/VideoAccess-VCMP/api/app/lib/models/mirror_camera.js index d74940f..edf920a 100644 --- a/code/VideoAccess-VCMP/api/app/lib/models/mirror_camera.js +++ b/code/VideoAccess-VCMP/api/app/lib/models/mirror_camera.js @@ -28,13 +28,13 @@ module.exports = dc => { model: "camera" } }, - mirrorIds: { + treeIds: { type: DataTypes.ARRAY(DataTypes.INTEGER), allowNull: false, defaultValue: null, comment: null, primaryKey: false, - field: "mirror_ids", + field: "tree_ids", autoIncrement: false }, filterIds: { @@ -45,6 +45,19 @@ module.exports = dc => { primaryKey: false, field: "filter_ids", autoIncrement: false + }, + mirrorId: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "mirror_id", + autoIncrement: false, + references: { + key: "id", + model: "mirror" + } } }, { tableName: "mirror_camera", diff --git a/code/VideoAccess-VCMP/api/app/lib/routes/mirror/index.js b/code/VideoAccess-VCMP/api/app/lib/routes/mirror/index.js new file mode 100644 index 0000000..4beb467 --- /dev/null +++ b/code/VideoAccess-VCMP/api/app/lib/routes/mirror/index.js @@ -0,0 +1,23 @@ +'use strict'; + +const mirror = require('../../controllers/mirror'); + +module.exports = function (app, router, opts) { + app.fs.api.logAttr['PUT/mirror'] = { content: '编辑镜像信息', visible: false }; + router.put('/mirror', mirror.edit); + + app.fs.api.logAttr['GET/mirror/list'] = { content: '获取镜像信息列表', visible: false }; + router.get('/mirror/list', mirror.list); + + app.fs.api.logAttr['GET/mirror/:mid'] = { content: '获取指定镜像信息', visible: false }; + router.get('/mirror/:mid', mirror.get); + + app.fs.api.logAttr['DEL/mirror/:mirrorId'] = { content: '删除镜像信息', visible: false }; + router.del('/mirror/:mirrorId', mirror.del); + + app.fs.api.logAttr['PUT/mirror/:mirrorId/publish'] = { content: '发布镜像信息', visible: false }; + router.put('/mirror/:mirrorId/publish', mirror.publish); + + app.fs.api.logAttr['PUT/mirror/:mirrorId/copy'] = { content: '复制镜像信息', visible: false }; + router.put('/mirror/:mirrorId/copy', mirror.copy); +}; diff --git a/code/VideoAccess-VCMP/api/sequelize-automate.config.js b/code/VideoAccess-VCMP/api/sequelize-automate.config.js index 0fd22cd..5ebd0fc 100644 --- a/code/VideoAccess-VCMP/api/sequelize-automate.config.js +++ b/code/VideoAccess-VCMP/api/sequelize-automate.config.js @@ -26,7 +26,7 @@ module.exports = { dir: './app/lib/models', // 指定输出 models 文件的目录 typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义 emptyDir: false, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir` - tables: ['mirror','mirror_camera','mirror_filter','mirror_filter_group','mirror_tree'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 + tables: ['mirror',], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性 tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中 ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面