20260328:数据推送

This commit is contained in:
George 2026-03-28 11:24:59 +08:00
parent c9620a344c
commit eb45296a57
4 changed files with 40 additions and 46 deletions

View File

@ -8,9 +8,9 @@ package com.fastbee.common.enums;
*/ */
public enum DataLevelEnum { public enum DataLevelEnum {
GREEN(1, "正常", "#009900"), GREEN(1, "绿色(正常)", "#009900"),
YELLOW(2, "告警", "#FF9900"), YELLOW(2, "黄色(告警)", "#FF9900"),
RED(3, "警报", "#FF0000"); RED(3, "红色(警报)", "#FF0000");
private final int code; private final int code;
private final String label; private final String label;

View File

@ -203,7 +203,7 @@ public class ThingsModelPushHandler {
} }
} }
// 与 WeChatPushServiceImpl 保持一致的 map 迭代方式 // 与微信模板消息要求保持一致的 map 迭代方式
Map<String, Object> sendMap = new LinkedHashMap<>(); Map<String, Object> sendMap = new LinkedHashMap<>();
for (Map.Entry<String, String> m : map.entrySet()) { for (Map.Entry<String, String> m : map.entrySet()) {
sendMap.put(m.getKey(), new TemplateDataVo(m.getValue(), level.getColor())); sendMap.put(m.getKey(), new TemplateDataVo(m.getValue(), level.getColor()));

View File

@ -75,16 +75,3 @@ VALUES (3434, '推送配置删除', 3430, 4, '#', '', NULL, 1, 0, 'F', '0', '0',
INSERT INTO `sys_menu` (menu_id, menu_name, parent_id, order_num, `path`, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) INSERT INTO `sys_menu` (menu_id, menu_name, parent_id, order_num, `path`, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
VALUES (3435, '手动立即推送', 3430, 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'iot:push:push', '#', 'admin', now(), 'admin', now(), ''); VALUES (3435, '手动立即推送', 3430, 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'iot:push:push', '#', 'admin', now(), 'admin', now(), '');
-- ============================================================
-- 升级脚本:如已执行过旧版本建表,执行以下语句迁移字段
-- ============================================================
ALTER TABLE `iot_things_model_push_config`
ADD COLUMN IF NOT EXISTS `template_id` BIGINT DEFAULT NULL COMMENT '通知模板ID(关联 notify_template.id)' AFTER `serial_number`,
ADD INDEX IF NOT EXISTS `idx_template_id` (`template_id`);
-- 迁移完成后可删除旧字段(按需执行):
-- ALTER TABLE `iot_things_model_push_config`
-- DROP COLUMN IF EXISTS `channel_id`,
-- DROP COLUMN IF EXISTS `wx_template_id`,
-- DROP COLUMN IF EXISTS `wx_msg_content`,
-- DROP COLUMN IF EXISTS `redirect_url`;

View File

@ -13,7 +13,7 @@
clearable @keyup.enter.native="handleQuery" style="width:160px" /> clearable @keyup.enter.native="handleQuery" style="width:160px" />
</el-form-item> </el-form-item>
<el-form-item label="状态" prop="status"> <el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width:100px"> <el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width:150px">
<el-option label="启用" :value="1" /> <el-option label="启用" :value="1" />
<el-option label="停用" :value="0" /> <el-option label="停用" :value="0" />
</el-select> </el-select>
@ -91,9 +91,8 @@
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="选择设备" prop="deviceId"> <el-form-item label="选择设备" prop="deviceId">
<el-select v-model="form.deviceId" placeholder="输入设备名称或编号搜索" <el-select v-model="form.deviceId" placeholder="请选择设备(可输入名称或编号筛选)"
filterable remote :remote-method="remoteSearchDevice" filterable clearable :loading="deviceOptionsLoading" style="width:100%"
:loading="deviceSearchLoading" style="width:100%"
@change="onDeviceChange"> @change="onDeviceChange">
<el-option v-for="d in deviceOptions" :key="d.deviceId" <el-option v-for="d in deviceOptions" :key="d.deviceId"
:label="deviceLabel(d)" :value="d.deviceId"> :label="deviceLabel(d)" :value="d.deviceId">
@ -212,7 +211,7 @@ import {
listPushConfig, getPushConfig, addPushConfig, updatePushConfig, listPushConfig, getPushConfig, addPushConfig, updatePushConfig,
changePushConfigStatus, delPushConfig, manualPush changePushConfigStatus, delPushConfig, manualPush
} from '@/api/iot/pushConfig'; } from '@/api/iot/pushConfig';
import { listDevice } from '@/api/iot/device'; import { listDeviceShort } from '@/api/iot/device';
import { listModel } from '@/api/iot/model'; import { listModel } from '@/api/iot/model';
import { listTemplate } from '@/api/notify/template'; import { listTemplate } from '@/api/notify/template';
import { listUser } from '@/api/system/user'; import { listUser } from '@/api/system/user';
@ -250,7 +249,7 @@ export default {
pushInterval: [{ required: true, message: '请填写推送间隔', trigger: 'blur' }] pushInterval: [{ required: true, message: '请填写推送间隔', trigger: 'blur' }]
}, },
deviceOptions: [], deviceOptions: [],
deviceSearchLoading: false, deviceOptionsLoading: false,
templateOptions: [], templateOptions: [],
thingsModelOptions: [], thingsModelOptions: [],
userOptions: [] userOptions: []
@ -276,25 +275,33 @@ export default {
this.getList(); this.getList();
}, },
remoteSearchDevice(keyword) { /**
if (!keyword) return; * 弹窗打开时拉取设备列表。必须用 shortList:/iot/device/list 的 SQL 不查 device_type,
this.deviceSearchLoading = true; * 返回的 deviceType 恒为 null,前端按 1/2 过滤会清空列表(远程搜索同样会失效)。
const baseQuery = { pageNum: 1, pageSize: 30 }; * shortList 与设备首页一致,含产品 device_type;entrance=1 排除网关子设备类产品(4)。
// 关键字同时匹配 deviceName 和 serialNumber */
const byName = listDevice({ ...baseQuery, deviceType: null, deviceName: keyword }); loadDeviceOptions(mergeDevice) {
const bySn = listDevice({ ...baseQuery, deviceType: null, serialNumber: keyword }); this.deviceOptions = [];
Promise.all([byName, bySn]).then(([r1, r2]) => { this.deviceOptionsLoading = true;
const rows1 = r1.rows || []; listDeviceShort({
const rows2 = r2.rows || []; pageNum: 1,
// 合并去重(以 deviceId 为 key),只保留直连(1)和网关(2)设备 pageSize: 500,
entrance: 1,
showChild: true
}).then(res => {
const map = new Map(); const map = new Map();
[...rows1, ...rows2].forEach(d => { (res.rows || []).forEach(d => {
if ((d.deviceType === 1 || d.deviceType === 2) && !map.has(d.deviceId)) { if ((d.deviceType === 1 || d.deviceType === 2) && d.deviceId != null && !map.has(d.deviceId)) {
map.set(d.deviceId, d); map.set(d.deviceId, d);
} }
}); });
if (mergeDevice && mergeDevice.deviceId != null && !map.has(mergeDevice.deviceId)) {
map.set(mergeDevice.deviceId, mergeDevice);
}
this.deviceOptions = Array.from(map.values()); this.deviceOptions = Array.from(map.values());
}).finally(() => { this.deviceSearchLoading = false; }); }).catch(() => {
this.$message.error('加载设备列表失败,请确认拥有设备查询权限');
}).finally(() => { this.deviceOptionsLoading = false; });
}, },
deviceLabel(d) { deviceLabel(d) {
@ -352,10 +359,10 @@ export default {
handleAdd() { handleAdd() {
this.form = emptyForm(); this.form = emptyForm();
this.deviceOptions = [];
this.thingsModelOptions = []; this.thingsModelOptions = [];
this.dialogTitle = '新增推送配置'; this.dialogTitle = '新增推送配置';
this.dialogOpen = true; this.dialogOpen = true;
this.loadDeviceOptions();
this.$nextTick(() => this.resetForm('form')); this.$nextTick(() => this.resetForm('form'));
}, },
handleUpdate(row) { handleUpdate(row) {
@ -366,14 +373,14 @@ export default {
userIds: data.userIds || [], userIds: data.userIds || [],
items: data.items || [] items: data.items || []
}; };
if (data.deviceId) { const mergeDev = data.deviceId ? {
this.deviceOptions = [{ deviceId: data.deviceId,
deviceId: data.deviceId, deviceName: data.deviceName || data.serialNumber,
deviceName: data.deviceName || data.serialNumber, serialNumber: data.serialNumber || '',
serialNumber: data.serialNumber || '', deviceType: data.deviceType || 1,
deviceType: data.deviceType || 1 productId: data.productId
}]; } : null;
} this.loadDeviceOptions(mergeDev);
if (data.productId) { if (data.productId) {
listModel({ productId: data.productId, type: 1, pageNum: 1, pageSize: 500 }).then(r => { listModel({ productId: data.productId, type: 1, pageNum: 1, pageSize: 500 }).then(r => {
this.thingsModelOptions = r.rows || []; this.thingsModelOptions = r.rows || [];