'use strict'; const fs = require('fs'); const moment = require('moment') async function getCameraProject (ctx, next) { try { const models = ctx.fs.dc.models; const { limit, page, orderBy, orderDirection, keyword, abilityId, type, venderId, nvrId, externalDomain, state, serialNo } = ctx.query const { userId, token } = ctx.fs.api const { utils: { getPlayUrl } } = ctx.app.fs let findOption = { attributes: { exclude: ['delete', 'recycleTime',] }, where: { createUserId: userId, recycleTime: null, delete: false }, order: [ [orderBy || 'id', orderDirection || 'DESC'] ], include: [{ model: models.CameraKind }, { model: models.Nvr, where: nvrId ? { id: nvrId } : {}, required: Boolean(nvrId), attributes: ['id', 'name', 'serialNo'] }, { model: models.SecretYingshi, attributes: ['token'] }, { model: models.CameraRemark, attributes: ['id', 'remark'], order: ['id'] }], distinct: true } let abilityFind = { model: models.CameraAbility } let gbCameraOption = { model: models.GbCamera, attributes: ['id', 'online', 'playUrl', 'did'], required: false } if (limit) { findOption.limit = limit } if (page && limit) { findOption.offset = page * limit } if (keyword) { // findOption.where.name = { $like: `%${keyword}%` } findOption.where.$or = [ { name: { $like: `%${keyword}%` } }, // { // serialNo: { $like: `%${keyword}%` } // } ] } if (type) { findOption.where.type = type } if (venderId) { findOption.where.venderId = venderId } if (abilityId) { abilityFind.where = { abilityId: abilityId } } if (externalDomain) { findOption.where.externalDomain = externalDomain } if (serialNo) { findOption.where['$or'] = { serialNo: { $in: serialNo.split(',') }, topSerialNo: { $in: serialNo.split(',') } } } if (state) { if (state == 'DISABLED') { findOption.where.forbidden = true } else { findOption.where.forbidden = false const onLineMap = { ON: ['ON', 'ONLINE'], OFF: ['OFF'], // UNKONW: [], // DISABLED: [] } let unknowState = [] for (let k in onLineMap) { unknowState = unknowState.concat(onLineMap[k]) } gbCameraOption.where = { online: state == 'UNKONW' ? { $notIn: unknowState } : { $in: onLineMap[state] } } gbCameraOption.required = true } } findOption.include.push(gbCameraOption) findOption.include.push(abilityFind) const cameraRes = await models.Camera.findAll(findOption) // const cameraRes = await models.Camera.findAndCountAll(findOption) delete findOption.order delete findOption.limit delete findOption.offset delete findOption.attributes const total = await models.Camera.count(findOption) let cameraIds = [] let createUserIds = new Set() for (let c of cameraRes) { cameraIds.push(c.dataValues.id) createUserIds.add(c.dataValues.createUserId) } // 查在安心云绑定的数据 const axbindCameraRes = await ctx.app.fs.axyRequest.get('vcmp/camera/project', { query: { token, cameraId: cameraIds.join(',') } }) // 查对应创建者信息 const corUsers = createUserIds.size ? await ctx.app.fs.authRequest.get(`user/${[...createUserIds].join(',') || -1}/message`, { query: { token } }) : [] for (let { dataValues: camera } of cameraRes) { const corBindCamera = axbindCameraRes.filter(b => b.cameraId == camera.id) camera.station = corBindCamera.reduce((station, c) => { return station.concat.apply(station, c.stations) }, []) const corUser = corUsers.find(u => u.id == camera.createUserId) camera.createUser = { namePresent: corUser ? corUser.namePresent : '' } // if (camera.type != 'yingshi') { // const playUrl = await getPlayUrl({ topSerialNo: camera.topSerialNo, serialNo: camera.serialNo }) // camera.gbCamera.dataValues.playUrl = playUrl // } } ctx.status = 200; ctx.body = { total: total, data: cameraRes } } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; ctx.body = {} } } async function getCamera (ctx) { try { const { models } = ctx.fs.dc; const { cameraId } = ctx.query const cameraRes = await models.Camera.findAll({ attributes: { exclude: ['delete', 'recycleTime',] }, where: { id: { $in: cameraId.split(',') } }, include: [{ model: models.CameraAbility }, { model: models.CameraKind }, { model: models.Vender }, { 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; ctx.body = cameraRes } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; ctx.body = {} } } async function detail (ctx) { let errMsg = `获取摄像头详情失败` try { const { models } = ctx.fs.dc; const { cameraId } = ctx.params const { userId, token } = ctx.fs.api const { utils: { getPlayUrl } } = ctx.app.fs const cameraRes = await models.Camera.findOne({ where: { id: cameraId }, include: { model: models.GbCamera, attributes: ['id', 'online', 'playUrl'], required: false } }) if (!cameraRes) { throw errMsg } const cameraProject = await ctx.app.fs.axyRequest.get('vcmp/camera/project', { query: { token, cameraId: cameraRes.id } }) const bindStations = [] for (let c of cameraProject) { for (let s of c.stations) { bindStations.push(s) } } const corUser = await ctx.app.fs.authRequest.get(`user/${cameraRes.createUserId}/message`, { query: { token } }) let rslt = { ...cameraRes.dataValues, station: bindStations, createUser: { namePresent: corUser.length && corUser[0].namePresent ? corUser[0].namePresent : '' } } if (cameraRes.type != 'yingshi') { rslt.gbCamera.playUrl = await getPlayUrl({ topSerialNo: cameraRes.topSerialNo, serialNo: cameraRes.serialNo }) } ctx.status = 200; ctx.body = rslt } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; ctx.body = { message: errMsg } } } async function getCameraListAll (ctx) { try { const { models } = ctx.fs.dc; const { userId, token } = ctx.fs.api const cameraRes = await models.Camera.findAll({ attributes: { exclude: ['delete', 'recycleTime', 'rtmp', 'createUserId', 'nvrId', 'kindId', 'yingshiSecretId', 'gbId'] }, order: [['id', 'DESC']], where: { createUserId: userId, 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; ctx.body = cameraRes } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; ctx.body = {} } } async function getCmaeraUniqueConfig (ctx) { // 为 李玉 python 摄像头状态查询的摄像头列表查询 // 获取所有已配置的序列号+通道号唯一的摄像头 try { const { models } = ctx.fs.dc; const sequelize = ctx.fs.dc.orm; // TODO: 目前只获取 yingshi 的 const cameraRes = await sequelize.query(` SELECT DISTINCT ON ("serial_no", "channel_no") camera.id, "camera"."serial_no" AS "serialNo", "camera"."type" AS "type", "camera"."channel_no" AS "channelNo", "secretYingshi"."token" AS "yingshiToken", camera_status_offline_log."status" AS "status", camera_status_offline_log."time" AS "updateTime" FROM "camera" AS "camera" LEFT JOIN "secret_yingshi" AS "secretYingshi" ON "camera"."yingshi_secret_id" = "secretYingshi"."id" LEFT JOIN ( SELECT camera_id, MAX(time) AS time FROM camera_status_offline_log GROUP BY camera_id ) AS offlineLogMax ON offlineLogMax."camera_id" = "camera"."id" LEFT JOIN camera_status_offline_log ON camera_status_offline_log.time = offlineLogMax.time WHERE "camera"."delete" = false AND "camera"."type" = 'yingshi' AND "camera"."recycle_time" IS NULL; `) ctx.status = 200; ctx.body = cameraRes[0] } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; ctx.body = { message: typeof error == 'string' ? error : undefined } } } async function banned (ctx) { try { const { models } = ctx.fs.dc; const data = ctx.request.body; // TODO 向视频服务发送通知 // 库记录 await models.Camera.update({ forbidden: data.forbidden }, { where: { id: data.cameraId } }) ctx.status = 204; } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; ctx.body = {} } } async function del (ctx) { const transaction = await ctx.fs.dc.orm.transaction(); try { const { models } = ctx.fs.dc; const { cameraId } = ctx.params const { token } = ctx.fs.api await models.MirrorCamera.destroy({ where: { cameraId: cameraId }, transaction }) await models.CameraStatusPushMonitor.destroy({ where: { cameraId: cameraId }, transaction }) await models.CameraAbilityBind.destroy({ where: { cameraId: cameraId }, transaction }) await models.CameraRemark.destroy({ where: { cameraId: cameraId }, transaction }) await models.Camera.destroy({ where: { id: cameraId }, transaction }) if (cameraId) { await ctx.app.fs.axyRequest.delete('vcmp/camera/project', { query: { token, cameraId: cameraId } }) } 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 = {} } } async function cameraExport (ctx) { try { const { models } = ctx.fs.dc const { userId, token } = ctx.fs.api const { utils: { simpleExcelDown } } = ctx.app.fs const header = [{ title: "设备名称", key: "name", }, { title: "设备厂家", key: "vender", }, { title: "接入类型", key: "type", }, { title: "设备状态", key: "state", }, { title: "云台支持", key: "cloudControl", }, { title: "内存卡信息", key: "memoryCard", }, { title: "设备创建时间", key: "createTime", }, { title: "设备添加账号", key: "createUser", }, { title: "项目名称", key: "projectName", }, { title: "pcode", key: "pcode", }, { title: "结构物", key: "structure", }, { title: "测点", key: "stationName", }, { title: "监测因素", key: "factor", },]; const cameraRes = await models.Camera.findAll({ where: { createUserId: userId, recycleTime: null, delete: false }, include: [{ model: models.CameraAbility }, { model: models.CameraKind }, { model: models.Vender }] }) let cameraIds = [] let createUserIds = new Set() for (let c of cameraRes) { cameraIds.push(c.dataValues.id) createUserIds.add(c.dataValues.createUserId) } // 查在安心云绑定的数据 const axbindCameraRes = await ctx.app.fs.axyRequest.get('vcmp/camera/project', { query: { token, cameraId: cameraIds.join(',') } }) // 查对应创建者信息 const corUsers = await ctx.app.fs.authRequest.get(`user/${[...createUserIds].join(',') || -1}/message`, { query: { token } }) let exportData = [] let typeMap = { yingshi: '萤石云平台摄像头', nvr: 'NVR摄像头', ipc: 'IPC 网络摄像头', cascade: '不明厂家', } for (let { dataValues: camera } of cameraRes) { camera.vender = camera.vender ? camera.vender.name : '' camera.type = typeMap[camera.type] const corUser = corUsers.find(u => u.id == camera.createUserId) camera.createUser = corUser ? corUser.username : '' let stationName = new Set(), projectName = new Set(), pcode = new Set(), structure = new Set(), factor = new Set() const corBindCamera = axbindCameraRes.find(b => b.cameraId == camera.id) if (corBindCamera) { for (let station of corBindCamera.stations) { stationName.add(station.name) factor.add(station.factor.name) structure.add(station.structure.name) for (let project of station.structure.projects) { projectName.add(project.name) pcode.add(project.url) } } } camera.stationName = [...stationName].join('\r\n') camera.factor = [...factor].join('\r\n') camera.projectName = [...projectName].join('\r\n') camera.pcode = [...pcode].join('\r\n') camera.structure = [...structure].join('\r\n') exportData.push(camera) } const fileName = `摄像头信息列表_${userId}_${moment().format('YYYYMMDDHHmmss')}` + '.csv' const filePath = await simpleExcelDown({ data: exportData, header, fileName: fileName }) const fileData = fs.readFileSync(filePath); ctx.status = 200; ctx.set('Content-Type', 'application/x-xls'); ctx.set('Content-disposition', 'attachment; filename=' + encodeURI(fileName)); ctx.body = fileData; } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; ctx.body = {} } } async function getAbility (ctx) { try { const { models } = ctx.fs.dc; const abilityRes = await models.CameraAbility.findAll() ctx.status = 200; ctx.body = abilityRes } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; ctx.body = {} } } async function getKind (ctx) { try { const { models } = ctx.fs.dc; const kindRes = await models.CameraKind.findAll() ctx.status = 200; ctx.body = kindRes } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; ctx.body = {} } } async function remark (ctx) { const transaction = await ctx.fs.dc.orm.transaction(); let errMsg = undefined try { const { models } = ctx.fs.dc const { cameraId, remark, } = ctx.request.body; const cameraRes = await models.Camera.findOne({ where: { id: cameraId } }) if (!cameraRes) { errMsg = '摄像头不存在' } await models.CameraRemark.destroy({ where: { cameraId }, transaction }) await models.CameraRemark.bulkCreate(remark.map(r => { return { cameraId, remark: r } }), { 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: errMsg } } } module.exports = { getCameraProject, getCamera, getCameraListAll, getCmaeraUniqueConfig, detail, banned, del, cameraExport, getAbility, getKind, remark, };