Px(+et)0RA@u(n15(hWf;f5&$+w1lgY4#K_QbXhDJrdh#<;2sgPJvX`@KaxxZX(
zrny`s(eR*x1=@_^ribm^a}I^LvIt#2B>WK;K@b**g1FhFLPo~uHr?*-`NmmpEOTDn
zxpyYr^Y43}=Y2lk@AIDbyszLof8;u^9|eGG?O?%yf&)GkD727I0ey!T9Plln&jkMu
z2V$DG1Y{F{+X)o;6zz(_1sDc!6e1f;+ul760nH3s@juc@iY_^WT?XkFKfd%L@L6Q~C8iHIx&
z*=;(`y^{mbv?2Vh{21FKsj4?;
zb<%Mz*)3UHXcWM7oF^I@8m9H1J=;aH3V>>$-Bfr^j~5Wg@b#w5;sOmX?;mQBiUZ
z;3clFub+M{mHGm}gCercwC$HC;{r*EMP8F2JFpl9I;_!x*?YN8SLuiB?AE4I-v#?~+^v
zk#8jsc+51-KPHOFJAgOwiWMu$2U4j6An#N})_2(Uy2})_g}|#2`7Rg=J!%+6|K)(E
z1i&jG8ja3`>wX1tz9Q1pVcQ%3)z6X0TXfym1^6LUT3XfC*7kc=w{p1;Tvn-A>@H+7
z`$^vDy{7FZ2i
z8co~Y5>wT61aycT1z-r!4Dw7+Q7U&>)`@G{LWxA;w#?AbK>+1}K;VV;WO7#`k(lMC
z(}zgT5s^NUGX*%N6c<;ux3`~(YFZRz2Sk1e0+ohspS&iEI2Mb|VntWQjx~oJpq-ft;lPwH=PreznDqz8_cB
zS6pB#L{5gt%I%Kxb6$TX;_>)wdi@Q|hDd$Vw%aCK{8&3_+8UCZA#&n^6Tgm)=K7
zkHx7dt4zyk@mu`3HL;eiy9px41Hs@zZ?Su0IorrkRP-Xeo(JZNqO373>z!N`KMtaa
zuDgEZbE_y--lF#n5Yx0F0!M@_BQWpc4Jzjls=A&)lZgB{ZTj>@=TfO%02WO8NICvc
zKhZBj9w0UmSPPLO0OkvMY|@AN(Q6g&o`xcah4k)g^8OH50AS=G@9SE{OAuAnr2=^W
zF;jlAMPOsnaXy?#v8;#syi4tKf&zd~0ey!T9Plln&jbYrdMw`
literal 0
HcmV?d00001
diff --git a/code/VideoAccess-VCMP/web/client/assets/images/background/icon_phone.png b/code/VideoAccess-VCMP/web/client/assets/images/background/icon_phone.png
new file mode 100644
index 0000000000000000000000000000000000000000..770340a211fc19d10858f7449cf6b08622367ed3
GIT binary patch
literal 561
zcmV-10?z%3P)Px$>q$gGRA@u(Sv^baP!K(H-G{3`L+l>%O0}@FQTzo8cTFA^B3RgHBd#C_79uu6
zKw|>${Q~tBue4BGZT$n5HY!N&NS0Mx&^Xz|kHtxSX70>6XD6GvgbshG!|R7O@YxEg
z0;)hk2EM0-JkOheb9G|=#XzMXiH&d~x@X`V#?-?wJdozht$=Us7z0Pr1KPn6FuoVZ
z|0J>PG;qqmAOV*!W*f{ml7rsF5cR^jc?S9kINOWkp*Levf(`6oBMlNlj3>J&zPz8#xS4QjuBlmegtO0~!00000NkvXXu0mjfU_$-X
literal 0
HcmV?d00001
diff --git a/code/VideoAccess-VCMP/web/client/src/sections/offline/components/pushSideSheet.jsx b/code/VideoAccess-VCMP/web/client/src/sections/offline/components/pushSideSheet.jsx
index f02da93..fccf34e 100644
--- a/code/VideoAccess-VCMP/web/client/src/sections/offline/components/pushSideSheet.jsx
+++ b/code/VideoAccess-VCMP/web/client/src/sections/offline/components/pushSideSheet.jsx
@@ -126,13 +126,21 @@ function pushSideSheet (props) {
-
摄像头名称
+
{equipmentTest}
-
{pushData.pushWay=='email'?'邮件通知':'短信通知'}
+
{noticeTest}
From 50b5cdaeb164babbaeb7282b3bb167bb9093782b Mon Sep 17 00:00:00 2001
From: "gao.zhiyuan"
Date: Wed, 3 Aug 2022 13:33:40 +0800
Subject: [PATCH 3/3] =?UTF-8?q?=E4=BF=A1=E9=B8=BD=E6=8E=A8=E9=80=81=2080%?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../api/app/lib/controllers/status/push.js | 63 +++++--
code/VideoAccess-VCMP/api/app/lib/index.js | 17 +-
.../lib/models/camera_status_offline_log.js | 52 ++++++
.../api/app/lib/schedule/cameraStatePush.js | 170 +++++++++++++++++-
.../api/app/lib/schedule/freshYingshiMsg.js | 29 ++-
.../api/app/lib/schedule/index.js | 4 +-
.../api/app/lib/utils/push.js | 6 +-
.../api/sequelize-automate.config.js | 2 +-
8 files changed, 308 insertions(+), 35 deletions(-)
create mode 100644 code/VideoAccess-VCMP/api/app/lib/models/camera_status_offline_log.js
diff --git a/code/VideoAccess-VCMP/api/app/lib/controllers/status/push.js b/code/VideoAccess-VCMP/api/app/lib/controllers/status/push.js
index a2b4055..91f5253 100644
--- a/code/VideoAccess-VCMP/api/app/lib/controllers/status/push.js
+++ b/code/VideoAccess-VCMP/api/app/lib/controllers/status/push.js
@@ -11,20 +11,55 @@ async function edit (ctx) {
receiver, cameraId
} = ctx.request.body;
+
let configId_ = configId
+ let receiverSorted = receiver.sort()
+ let cameraIdSorted = cameraId.sort()
+ let noticeWaySorted = noticeWay.sort()
+ let editDataExist = false
// 判重
-
+ const configAllRes = await models.CameraStatusPushConfig.findAll({
+ include: [{
+ model: models.CameraStatusPushMonitor,
+ attributes: ['cameraId'],
+ required: false,
+ duplicating: true,
+ }, {
+ model: models.CameraStatusPushReceiver,
+ attributes: ['receiver'],
+ duplicating: false,
+ required: false,
+ },]
+ })
+ for (let c of configAllRes) {
+ if (configId_ && c.id == configId_) {
+ editDataExist = true
+ continue
+ } else if (c.name == name) {
+ throw '已有同名称配置信息'
+ }
+ const cReceiver = c.cameraStatusPushReceivers.map(r => r.receiver)
+ const cCameraId = c.cameraStatusPushMonitors.map(m => m.cameraId)
+ const cReceiverSorted = cReceiver.sort()
+ const cCameraIdSorted = cCameraId.sort()
+ if (
+ receiverSorted.join(',') == cReceiverSorted.join(',') && cameraIdSorted.join(',') == cCameraIdSorted.join(',') &&
+ c.pushWay == pushWay &&
+ c.noticeWay.sort().join(',') == noticeWaySorted.join(',') &&
+ (
+ (!c.timing && !timing) ||
+ c.timing == timing
+ )
+ ) {
+ throw '已有相同配置信息'
+ }
+ }
let config = {
name, pushWay, noticeWay, timing
}
if (configId) {
- const configRes = await models.CameraStatusPushConfig.findOne({
- where: {
- id: configId
- }
- })
- if (!configRes) {
+ if (!editDataExist) {
throw '参数错误'
} else {
// DO UPDATE
@@ -89,7 +124,7 @@ async function get (ctx) {
try {
const models = ctx.fs.dc.models;
const { userId, token } = ctx.fs.api
- const { limit, page, orderBy, orderDirection, keyword, pushWay } = ctx.query
+ const { limit, page, orderBy, orderDirection, name, pushWay } = ctx.query
const sequelize = ctx.fs.dc.ORM;
let findOption = {
@@ -104,13 +139,16 @@ async function get (ctx) {
if (page && limit) {
findOption.offset = page * limit
}
- if (keyword) {
+ if (name) {
findOption.where['$or'] = {
name: {
- $like: `%${keyword}%`
+ $like: `%${name}%`
},
}
}
+ if (pushWay) {
+ findOption.where.pushWay = pushWay
+ }
const configLimitRes = await models.CameraStatusPushConfig.findAll(findOption)
@@ -131,6 +169,7 @@ async function get (ctx) {
'cameraStatusPushMonitors.id',
'cameraStatusPushLogs.id',
'cameraStatusPushReceivers.id',
+ 'cameraStatusPushMonitors->camera.id',
]
findOption.order = [
[orderBy || 'id', orderDirection || 'DESC']
@@ -141,6 +180,10 @@ async function get (ctx) {
attributes: ['cameraId'],
required: false,
duplicating: true,
+ include: [{
+ model: models.Camera,
+ attributes: ['name'],
+ }]
},
{
model: models.CameraStatusPushLog,
diff --git a/code/VideoAccess-VCMP/api/app/lib/index.js b/code/VideoAccess-VCMP/api/app/lib/index.js
index b50280c..3b9484e 100644
--- a/code/VideoAccess-VCMP/api/app/lib/index.js
+++ b/code/VideoAccess-VCMP/api/app/lib/index.js
@@ -46,15 +46,6 @@ module.exports.entry = function (app, router, opts) {
module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Sequelize, models: {} }
// 加载定义模型 历史写法
// require('./models/nvr')(dc);
- // require('./models/camera_ability')(dc);
- // require('./models/camera_kind')(dc);
- // require('./models/camera')(dc);
- // require('./models/camera_ability_bind')(dc);
- // require('./models/vender')(dc);
- // require('./models/secret_yingshi')(dc);
- // require('./models/gb_camera')(dc);
- // require('./models/ax_project')(dc);
- // require('./models/camera_remark')(dc);
// 模型关系摘出来 初始化之后再定义关系才行
fs.readdirSync(path.join(__dirname, '/models')).forEach((filename) => {
@@ -64,7 +55,7 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
const {
Nvr, Camera, CameraAbility, CameraAbilityBind, CameraKind, CameraRemark,
GbCamera, SecretYingshi, Vender, CameraStatus, CameraStatusResolve, CameraStatusLog,
- CameraStatusPushConfig, CameraStatusPushMonitor, CameraStatusPushLog, CameraStatusPushReceiver,
+ CameraStatusPushConfig, CameraStatusPushMonitor, CameraStatusPushLog, CameraStatusPushReceiver, CameraStatusOfflineLog
} = dc.models;
// Nvr.belongsTo(User, { foreignKey: 'userId', targetKey: 'id' });
@@ -105,9 +96,15 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
CameraStatusPushMonitor.belongsTo(CameraStatusPushConfig, { foreignKey: 'configId', targetKey: 'id' });
CameraStatusPushConfig.hasMany(CameraStatusPushMonitor, { foreignKey: 'configId', sourceKey: 'id' });
+ CameraStatusPushMonitor.belongsTo(Camera, { foreignKey: 'cameraId', targetKey: 'id' });
+ Camera.hasMany(CameraStatusPushMonitor, { foreignKey: 'cameraId', sourceKey: 'id' });
+
CameraStatusPushLog.belongsTo(CameraStatusPushConfig, { foreignKey: 'pushConfigId', targetKey: 'id' });
CameraStatusPushConfig.hasMany(CameraStatusPushLog, { foreignKey: 'pushConfigId', sourceKey: 'id' });
CameraStatusPushReceiver.belongsTo(CameraStatusPushConfig, { foreignKey: 'configId', targetKey: 'id' });
CameraStatusPushConfig.hasMany(CameraStatusPushReceiver, { foreignKey: 'configId', sourceKey: 'id' });
+
+ CameraStatusOfflineLog.belongsTo(Camera, { foreignKey: 'cameraId', targetKey: 'id' });
+ Camera.hasMany(CameraStatusOfflineLog, { foreignKey: 'cameraId', sourceKey: 'id' });
};
diff --git a/code/VideoAccess-VCMP/api/app/lib/models/camera_status_offline_log.js b/code/VideoAccess-VCMP/api/app/lib/models/camera_status_offline_log.js
new file mode 100644
index 0000000..5d27b2c
--- /dev/null
+++ b/code/VideoAccess-VCMP/api/app/lib/models/camera_status_offline_log.js
@@ -0,0 +1,52 @@
+/* eslint-disable*/
+'use strict';
+
+module.exports = dc => {
+ const DataTypes = dc.ORM;
+ const sequelize = dc.orm;
+ const CameraStatusOfflineLog = sequelize.define("cameraStatusOfflineLog", {
+ id: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: null,
+ comment: null,
+ primaryKey: true,
+ field: "id",
+ autoIncrement: true,
+ unique: "camera_status_offline_log_id_uindex"
+ },
+ cameraId: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: null,
+ comment: null,
+ primaryKey: false,
+ field: "camera_id",
+ autoIncrement: false
+ },
+ status: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ defaultValue: null,
+ comment: null,
+ primaryKey: false,
+ field: "status",
+ autoIncrement: false
+ },
+ time: {
+ type: DataTypes.DATE,
+ allowNull: false,
+ defaultValue: null,
+ comment: null,
+ primaryKey: false,
+ field: "time",
+ autoIncrement: false
+ }
+ }, {
+ tableName: "camera_status_offline_log",
+ comment: "",
+ indexes: []
+ });
+ dc.models.CameraStatusOfflineLog = CameraStatusOfflineLog;
+ return CameraStatusOfflineLog;
+};
\ No newline at end of file
diff --git a/code/VideoAccess-VCMP/api/app/lib/schedule/cameraStatePush.js b/code/VideoAccess-VCMP/api/app/lib/schedule/cameraStatePush.js
index 72c389e..252b305 100644
--- a/code/VideoAccess-VCMP/api/app/lib/schedule/cameraStatePush.js
+++ b/code/VideoAccess-VCMP/api/app/lib/schedule/cameraStatePush.js
@@ -3,16 +3,178 @@ const moment = require('moment')
module.exports = function (app, opts) {
const cameraOnlinePush = app.fs.scheduleInit(
{
- // interval: '* * 4 * * *',
- interval: '*/15 * * * *',
+ interval: '* */15 * * * *',
+ // interval: '*/15 * * * * *',
immediate: false,
+ proRun: false,
},
async () => {
try {
-
+ const { models } = app.fs.dc
+ const { pushBySms, pushByEmail } = app.fs.utils
+ const configRes = await models.CameraStatusPushConfig.findAll({
+ where: {
+ forbidden: false
+ },
+ include: [
+ {
+ model: models.CameraStatusPushMonitor,
+ attributes: ['cameraId'],
+ required: false,
+ duplicating: true
+ },
+ {
+ model: models.CameraStatusPushReceiver,
+ attributes: ['receiver'],
+ duplicating: false,
+ required: false,
+ },
+ ],
+ })
+
+ const timeNow = moment().format()
+ for (let c of configRes) {
+ // 查配置信息所对应的摄像头15min内的在离线状态
+ const cameraIds = c.cameraStatusPushMonitors.map(m => m.cameraId)
+ const offlineStatusRes = await models.CameraStatusOfflineLog.findAll({
+ where: {
+ cameraId: { $in: cameraIds },
+ time: {
+ $between: [moment(timeNow).subtract(15, 'minutes').format(), timeNow]
+ }
+ },
+ include: [{
+ model: models.Camera,
+ attributes: ['name']
+ }],
+ order: [['time', 'ASC']],
+ })
+
+
+
+ if (offlineStatusRes.length) {
+ const cameraStatusMap = {}
+ // 当前逻辑
+ // 只要最后状态是离线 就做离线推送
+ // 只要出现一次上线 就做上线推送
+ for (let s of offlineStatusRes) {
+ if (cameraStatusMap[s.cameraId]) {
+ cameraStatusMap.status.push({
+ cameraId: s.cameraId,
+ status: s.status,
+ time: s.time,
+ })
+ } else {
+ cameraStatusMap[s.cameraId] = {
+ status: [{
+ cameraId: s.cameraId,
+ status: s.status,
+ time: s.time,
+ }],
+ name: s.camera.name,
+ }
+ }
+ }
+ let offArr = []
+ let onArr = []
+ for (let k in Object.keys(cameraStatusMap).sort()) {
+ const data = cameraStatusMap[k]
+ if (data.status[0].status == 'OFF') {
+ offArr.push({
+ name: data.name,
+ time: data.status[0].time,
+ })
+ }
+ const onLineIndex = data.status.findIndex(s => s.status == 'ON')
+ if (onLineIndex >= 0) {
+ const onLineLastOffIndex = data.status.findIndex((s, i) => s.status == 'OFF' && i > onLineIndex)
+ let onlineData = {
+ cameraId: data.status[onLineIndex].cameraId,
+ name: data.status[onLineIndex].name,
+ time: data.status[onLineIndex].time,
+ }
+ if (onLineLastOffIndex >= 0) {
+ onlineData.offTime = data.status[onLineLastOffIndex].time
+ }
+ onArr.push(onlineData)
+ }
+ }
+
+ // 查当前配置由谁配置
+ const corUser = await app.fs.authRequest.get(`user/${c.createUser}/message`, {
+ query: {
+ // TODO 这里拿不到 token 去鉴权 怎么能系统间鉴权呢
+ // 暂时取消了鉴权系统对这里的判断
+ token: ''
+ }
+ })
+
+ const receiver = c.cameraStatusPushReceivers.map(r => r.receiver)
+ // 离线推送
+ if (offArr.length && c.noticeWay && c.noticeWay.includes('offline')) {
+ if (c.pushWay == 'email') {
+ // 邮件
+ let text = `【${corUser[0].namePresent}】账号下的设备,截止【${moment(timeNow).format('YYYY年HH时mm分')}】,有${offArr.length}个设备掉线:\n`
+ text += offArr.map(o => `【${o.name}】于【${o.time}】掉线,`).join('\n')
+ text += `\n请及时处理!`
+ await pushByEmail({
+ email: receiver,
+ title: '',
+ text,
+ })
+ } else if (c.pushWay == 'phone') {
+ // 短信
+ let text = `【${corUser[0].namePresent}】账号下的`
+ if (offArr.length == 1) {
+ text += `【${offArr[0].name}】离线,请及时处理!【${moment(offArr[0].time).format('YYYY年HH时mm分')}】`
+ } else {
+ text += offArr.map(o => `【${o.name}】`).join('')
+ if (text.length > 35) {
+ text = text.substring(0, 35) + '...'
+ text += `等${offArr.length}个摄像头离线`
+ }
+ text += `,请及时处理!【${moment().format('MM月DD日HH时mm分')}-${moment(timeNow).subtract(15, 'minutes').format('MM月DD日HH时mm分')}】`
+ }
+ await pushBySms({
+ phone: receiver,
+ templateCode: '',
+ templateParam: '',
+ text
+ })
+ }
+ }
+
+ // 上线推送
+ if (onArr.length && c.noticeWay && c.noticeWay.includes('online')) {
+ if (c.pushWay == 'email') {
+ // 邮件
+ // const outTimeCameraOff = onArr.filter(a => !a.offTime)
+ // if (outTimeCameraOff.length) {
+ // let cameraIds = outTimeCameraOff.map(a => a.cameraId)
+ // const lastOfflineRes = await models.CameraStatus.findAll({
+ // where: {
+ // cameraId: { $in: cameraIds },
+ // status: 'OFF',
+ // time: {
+ // $lt: moment(timeNow).subtract(15, 'minutes').format()
+ // }
+ // },
+ // group: ['cameraId'],
+ // attributes: ['cameraId', [sequelize.fn('max', sequelize.col('time')), 'time']],
+ // })
+
+ // }
+
+ } else if (c.pushWay == 'phone') {
+ // 短信
+ }
+ }
+ }
+ }
+
console.log('cameraOnlinePush')
} catch (error) {
-
+
}
}
)
diff --git a/code/VideoAccess-VCMP/api/app/lib/schedule/freshYingshiMsg.js b/code/VideoAccess-VCMP/api/app/lib/schedule/freshYingshiMsg.js
index b6dd362..941015e 100644
--- a/code/VideoAccess-VCMP/api/app/lib/schedule/freshYingshiMsg.js
+++ b/code/VideoAccess-VCMP/api/app/lib/schedule/freshYingshiMsg.js
@@ -4,9 +4,10 @@ const moment = require('moment')
module.exports = function (app, opts) {
const freshYingshiState = app.fs.scheduleInit(
{
- // interval: '* * 4 * * *',
- interval: '*/10 * * * *',
+ interval: '* */5 * * * *',
+ // interval: '*/10 * * * *',
immediate: false,
+ proRun: true,
},
async () => {
@@ -49,22 +50,39 @@ module.exports = function (app, opts) {
if (existD.online != storageD.online) {
// 状态更新
if (storageD.online == 'ON' && !existD.playUrl) {
+ // 播放地址更新
const playUrlRes = await getYingshiPlayUrl({ deviceSerial: d.deviceSerial, token: tokenYingshi })
storageD.playUrl = playUrlRes
}
+ // 在离线状态更新
await models.GbCamera.update(storageD, {
where: {
id: existD.id
}
})
- // 状态更新 END
-
- // 状态推送
+ // 状态向前端页面推送
cameraStatePush({
gbId: existD.id,
online: storageD.online,
ipctype: storageD.ipctype,
})
+ // 记录日志
+ const cameraRes = await models.Camera.findAll({
+ where: {
+ gbId: existD.id
+ }
+ })
+ const nowTime = moment().format()
+ let bulkCreateData = cameraRes.map(c => {
+ return {
+ cameraId: c.id,
+ status: storageD.online,
+ time: nowTime
+ }
+ })
+ if (bulkCreateData.length) {
+ await models.CameraStatusOfflineLog.bulkCreate(bulkCreateData)
+ }
}
} else {
const yingshiRes = await models.GbCamera.create(storageD)
@@ -99,6 +117,7 @@ module.exports = function (app, opts) {
// interval: '*/30 * * * *',
interval: '0 34 5 1 * *',
immediate: true,
+ proRun: true,
},
async () => {
try {
diff --git a/code/VideoAccess-VCMP/api/app/lib/schedule/index.js b/code/VideoAccess-VCMP/api/app/lib/schedule/index.js
index d28db8c..e8f4137 100644
--- a/code/VideoAccess-VCMP/api/app/lib/schedule/index.js
+++ b/code/VideoAccess-VCMP/api/app/lib/schedule/index.js
@@ -7,10 +7,10 @@ const nodeSchedule = require('node-schedule');
module.exports = async function (app, opts) {
const scheduleInit = ({
- interval, immediate
+ interval, immediate, proRun,
}, callback) => {
const j = nodeSchedule.scheduleJob(interval, callback);
- if (immediate && !opts.dev) {
+ if (immediate && (!proRun || (proRun && !opts.dev))) {
setTimeout(callback, 0)
}
return j;
diff --git a/code/VideoAccess-VCMP/api/app/lib/utils/push.js b/code/VideoAccess-VCMP/api/app/lib/utils/push.js
index a46dc9e..cb2e9b5 100644
--- a/code/VideoAccess-VCMP/api/app/lib/utils/push.js
+++ b/code/VideoAccess-VCMP/api/app/lib/utils/push.js
@@ -5,7 +5,7 @@ const Core = require('@alicloud/pop-core');
const nodemailer = require('nodemailer')
module.exports = function (app, opts) {
- const pushBySms = async ({ phone = [] } = {}) => {
+ const pushBySms = async ({ phone = [], templateCode, templateParam } = {}) => {
try {
if (phone.length) {
const client = new Core({
@@ -29,7 +29,7 @@ module.exports = function (app, opts) {
}
}
- const pushByEmail = async () => {
+ const pushByEmail = async ({ email = [], title, text = '', html = '', attachments = undefined } = {}) => {
try {
let transporter = nodemailer.createTransport({
host: opts.email.host,
@@ -44,7 +44,7 @@ module.exports = function (app, opts) {
// send mail with defined transport object
await transporter.sendMail({
from: 'grazing-stars@qq.com', // sender address
- to: email, // list of receivers 逗号分隔字符串
+ to: email.join(','), // list of receivers 逗号分隔字符串
subject: title, // Subject line
text: text, // plain text body
html: html, // html body
diff --git a/code/VideoAccess-VCMP/api/sequelize-automate.config.js b/code/VideoAccess-VCMP/api/sequelize-automate.config.js
index efc91da..aaf2887 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: ['camera_status_push_log'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性
+ tables: ['camera_status_offline_log'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性
skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性
tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中
ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面