From c9620a344c7457d957211f07163dbb14f328a24b Mon Sep 17 00:00:00 2001 From: zhugongping Date: Sat, 28 Mar 2026 10:38:52 +0800 Subject: [PATCH] =?UTF-8?q?20260326:=E6=95=B0=E6=8D=AE=E6=8E=A8=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fastbee/common/enums/DataLevelEnum.java | 6 +- .../iot/data/push/ThingsModelPushHandler.java | 191 ++++++++------- .../iot/domain/ThingsModelPushConfig.java | 24 +- .../iot/ThingsModelPushConfigMapper.xml | 91 ++++--- sql/iot_things_model_push.sql | 28 ++- yack-web/src/views/iot/push/index.vue | 231 +++++++++++------- 6 files changed, 327 insertions(+), 244 deletions(-) diff --git a/fastbee-common/src/main/java/com/fastbee/common/enums/DataLevelEnum.java b/fastbee-common/src/main/java/com/fastbee/common/enums/DataLevelEnum.java index 2274cd88..7be892b8 100644 --- a/fastbee-common/src/main/java/com/fastbee/common/enums/DataLevelEnum.java +++ b/fastbee-common/src/main/java/com/fastbee/common/enums/DataLevelEnum.java @@ -8,9 +8,9 @@ package com.fastbee.common.enums; */ public enum DataLevelEnum { - GREEN(1, "✅ 正常", "#009900"), - YELLOW(2, "⚠️ 告警", "#FF9900"), - RED(3, "🔴 警报", "#FF0000"); + GREEN(1, "正常", "#009900"), + YELLOW(2, "告警", "#FF9900"), + RED(3, "警报", "#FF0000"); private final int code; private final String label; diff --git a/fastbee-iot-data/src/main/java/com/fastbee/iot/data/push/ThingsModelPushHandler.java b/fastbee-iot-data/src/main/java/com/fastbee/iot/data/push/ThingsModelPushHandler.java index 9e66b46c..7b083e00 100644 --- a/fastbee-iot-data/src/main/java/com/fastbee/iot/data/push/ThingsModelPushHandler.java +++ b/fastbee-iot-data/src/main/java/com/fastbee/iot/data/push/ThingsModelPushHandler.java @@ -1,9 +1,11 @@ package com.fastbee.iot.data.push; import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import com.fastbee.common.constant.FastBeeConstant; import com.fastbee.common.core.notify.NotifySendResponse; import com.fastbee.common.core.notify.config.WeChatConfigParams; +import com.fastbee.common.core.notify.msg.WechatMsgParams; import com.fastbee.common.core.redis.RedisCache; import com.fastbee.common.core.redis.RedisKeyBuilder; import com.fastbee.common.enums.DataLevelEnum; @@ -11,15 +13,17 @@ import com.fastbee.common.utils.StringUtils; import com.fastbee.common.utils.wechat.WechatUtils; import com.fastbee.common.wechat.WeChatAppResult; import com.fastbee.iot.domain.PushModelItem; +import com.fastbee.iot.domain.SocialUser; import com.fastbee.iot.domain.ThingsModelPushConfig; import com.fastbee.iot.model.ThingsModels.ValueItem; import com.fastbee.iot.service.IThingsModelPushConfigService; -import com.fastbee.notify.domain.NotifyChannel; +import com.fastbee.iot.service.ISocialUserService; import com.fastbee.notify.core.wechat.vo.TemplateDataVo; import com.fastbee.notify.core.wechat.vo.WeChatPublicAccountPushVO; +import com.fastbee.notify.domain.NotifyChannel; +import com.fastbee.notify.domain.NotifyTemplate; import com.fastbee.notify.service.INotifyChannelService; -import com.fastbee.iot.service.ISocialUserService; -import com.fastbee.iot.domain.SocialUser; +import com.fastbee.notify.service.INotifyTemplateService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.http.HttpEntity; @@ -28,28 +32,31 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; -import com.alibaba.fastjson2.JSONObject; import javax.annotation.Resource; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; /** * 物模型数据推送核心处理器 - * 读取 Redis 中设备最新上报值,按配置的阈值评估数据等级, - * 同等级数据合并为一条微信服务号模板消息推送。 - * - * @author fastbee + *

+ * 复用"运维管理 - 通知模板"中已配置的微信公众号模板(notify_template), + * 从中读取 templateId、content(变量)、redirectUrl 等信息。 */ @Slf4j @Component public class ThingsModelPushHandler { private static final RestTemplate REST_TEMPLATE = new RestTemplate(); + private static final Pattern VAR_PATTERN = Pattern.compile("\\{\\{([^}]+)}}"); @Resource private IThingsModelPushConfigService pushConfigService; @Resource + private INotifyTemplateService notifyTemplateService; + @Resource private INotifyChannelService notifyChannelService; @Resource private ISocialUserService socialUserService; @@ -57,20 +64,35 @@ public class ThingsModelPushHandler { private RedisCache redisCache; /** - * 按推送配置ID执行推送(读取该配置涉及设备的最新 Redis 值) - * - * @param config 推送配置(含 items、userIds) + * 按推送配置执行推送 */ public void doPush(ThingsModelPushConfig config) { if (CollectionUtils.isEmpty(config.getItems())) { log.warn("[Push] configId={} 没有配置物模型项,跳过", config.getId()); return; } + if (config.getDeviceId() == null || config.getDeviceId() <= 0 + || StringUtils.isEmpty(config.getSerialNumber())) { + log.warn("[Push] configId={} 未指定具体设备,跳过", config.getId()); + return; + } - // 获取通知渠道(公众号) - NotifyChannel channel = notifyChannelService.selectNotifyChannelById(config.getChannelId()); + // ---------- 从通知模板获取微信配置 ---------- + NotifyTemplate notifyTemplate = notifyTemplateService.selectNotifyTemplateById(config.getTemplateId()); + if (notifyTemplate == null) { + log.warn("[Push] configId={} 通知模板不存在 templateId={}", config.getId(), config.getTemplateId()); + return; + } + WechatMsgParams msgParams = JSONObject.parseObject(notifyTemplate.getMsgParams(), WechatMsgParams.class); + if (msgParams == null || StringUtils.isEmpty(msgParams.getTemplateId())) { + log.warn("[Push] configId={} 通知模板 msgParams 不完整", config.getId()); + return; + } + + // ---------- 从通知渠道获取 appId/appSecret ---------- + NotifyChannel channel = notifyChannelService.selectNotifyChannelById(notifyTemplate.getChannelId()); if (channel == null) { - log.warn("[Push] configId={} 渠道不存在 channelId={}", config.getId(), config.getChannelId()); + log.warn("[Push] configId={} 渠道不存在 channelId={}", config.getId(), notifyTemplate.getChannelId()); return; } WeChatConfigParams wxConfig = JSONObject.parseObject(channel.getConfigContent(), WeChatConfigParams.class); @@ -79,7 +101,7 @@ public class ThingsModelPushHandler { return; } - // 获取 access_token + // ---------- 获取 access_token ---------- WeChatAppResult tokenResult = WechatUtils.getAccessToken(wxConfig.getAppId(), wxConfig.getAppSecret()); if (tokenResult == null || StringUtils.isEmpty(tokenResult.getAccessToken())) { log.error("[Push] configId={} 获取微信 access_token 失败", config.getId()); @@ -87,14 +109,7 @@ public class ThingsModelPushHandler { } String pushUrl = FastBeeConstant.URL.WX_PUBLIC_ACCOUNT_TEMPLATE_SEND_URL_PREFIX + tokenResult.getAccessToken(); - // 确定要查询的设备编号列表 - List serialNumbers = resolveSerialNumbers(config); - if (CollectionUtils.isEmpty(serialNumbers)) { - log.warn("[Push] configId={} 没有找到对应设备,跳过", config.getId()); - return; - } - - // 获取接收用户 openId 列表 + // ---------- 获取接收用户 openId ---------- List userIds = config.getUserIds(); if (CollectionUtils.isEmpty(userIds)) { log.warn("[Push] configId={} 没有配置接收用户,跳过", config.getId()); @@ -103,28 +118,31 @@ public class ThingsModelPushHandler { List socialUsers = socialUserService.listWechatPublicAccountOpenId( userIds.stream().map(Object::toString).collect(Collectors.toList())); Map openIdMap = socialUsers.stream() + .filter(u -> StringUtils.isNotEmpty(u.getOpenId())) .collect(Collectors.toMap(SocialUser::getSysUserId, SocialUser::getOpenId, (a, b) -> b)); - - // 对每台设备推送 - for (String serialNumber : serialNumbers) { - pushForDevice(config, serialNumber, pushUrl, openIdMap); + if (openIdMap.isEmpty()) { + log.warn("[Push] configId={} 所有用户均未绑定微信公众号,跳过", config.getId()); + return; } + // ---------- 执行推送 ---------- + pushForDevice(config, msgParams, pushUrl, openIdMap); + // 更新最近推送时间 pushConfigService.updateLastPushTime(config.getId()); } /** - * 对单台设备执行推送 + * 对单台设备按等级分组推送 */ - private void pushForDevice(ThingsModelPushConfig config, String serialNumber, + private void pushForDevice(ThingsModelPushConfig config, WechatMsgParams msgParams, String pushUrl, Map openIdMap) { + String serialNumber = config.getSerialNumber().toUpperCase(); String redisKey = RedisKeyBuilder.buildTSLVCacheKey(config.getProductId(), serialNumber); - // 按等级分组收集显示文本 Map> levelTextMap = new LinkedHashMap<>(); - for (DataLevelEnum level : DataLevelEnum.values()) { - levelTextMap.put(level, new ArrayList<>()); + for (DataLevelEnum lvl : DataLevelEnum.values()) { + levelTextMap.put(lvl, new ArrayList<>()); } for (PushModelItem item : config.getItems()) { @@ -134,8 +152,7 @@ public class ThingsModelPushHandler { } String value; try { - ValueItem vi = JSON.parseObject(cacheValue, ValueItem.class); - value = vi.getValue(); + value = JSON.parseObject(cacheValue, ValueItem.class).getValue(); } catch (Exception e) { value = cacheValue; } @@ -143,57 +160,69 @@ public class ThingsModelPushHandler { value, item.getGreenMin(), item.getGreenMax(), item.getYellowMin(), item.getYellowMax()); - String displayName = StringUtils.isNotEmpty(item.getModelName()) ? item.getModelName() : item.getIdentifier(); - String text = displayName + "=" + value + (StringUtils.isNotEmpty(item.getUnit()) ? item.getUnit() : ""); + String displayName = StringUtils.isNotEmpty(item.getModelName()) + ? item.getModelName() : item.getIdentifier(); + String text = displayName + "=" + value + + (StringUtils.isNotEmpty(item.getUnit()) ? item.getUnit() : ""); levelTextMap.get(level).add(text); } - // 对每个非空等级发一条消息 for (Map.Entry> entry : levelTextMap.entrySet()) { if (CollectionUtils.isEmpty(entry.getValue())) { continue; } DataLevelEnum level = entry.getKey(); String dataText = String.join(";", entry.getValue()); - sendTemplateMsg(config, serialNumber, level, dataText, pushUrl, openIdMap); + sendTemplateMsg(config, msgParams, serialNumber, level, dataText, pushUrl, openIdMap); } } /** - * 发送单条微信模板消息(带颜色标记) + * 发送单条微信模板消息。 + * 从通知模板的 content 字段提取 {{变量.DATA}} 变量名顺序, + * 按顺序映射:第1个→设备名称,第2个→数据等级,第3个→监测数据。 */ - private void sendTemplateMsg(ThingsModelPushConfig config, String serialNumber, - DataLevelEnum level, String dataText, + private void sendTemplateMsg(ThingsModelPushConfig config, WechatMsgParams msgParams, + String serialNumber, DataLevelEnum level, String dataText, String pushUrl, Map openIdMap) { - // 微信模板格式(需在公众号后台与此对应): - // {{first.DATA}} - // 设备编号:{{keyword1.DATA}} - // 数据等级:{{keyword2.DATA}} - // 监测数据:{{keyword3.DATA}} - // {{remark.DATA}} - Map data = new LinkedHashMap<>(); - data.put("first", new TemplateDataVo("设备数据监测通知", "#333333")); - data.put("keyword1", new TemplateDataVo(serialNumber, "#333333")); - data.put("keyword2", new TemplateDataVo(level.getLabel(), level.getColor())); - data.put("keyword3", new TemplateDataVo(dataText, level.getColor())); - data.put("remark", new TemplateDataVo("请及时关注设备状态", "#999999")); + List varNames = extractVarNames(msgParams.getContent()); + + LinkedHashMap map = new LinkedHashMap<>(); + String deviceDisplay = StringUtils.isNotEmpty(config.getDeviceName()) + ? config.getDeviceName() : serialNumber; + for (int i = 0; i < varNames.size(); i++) { + String varName = varNames.get(i); + if (i == 0) { + map.put(varName, deviceDisplay); + } else if (i == 1) { + map.put(varName, level.getLabel()); + } else if (i == 2) { + map.put(varName, dataText); + } else { + map.put(varName, ""); + } + } + + // 与 WeChatPushServiceImpl 保持一致的 map 迭代方式 + Map sendMap = new LinkedHashMap<>(); + for (Map.Entry m : map.entrySet()) { + sendMap.put(m.getKey(), new TemplateDataVo(m.getValue(), level.getColor())); + } for (Map.Entry userEntry : openIdMap.entrySet()) { String openId = userEntry.getValue(); - if (StringUtils.isEmpty(openId)) { - continue; - } WeChatPublicAccountPushVO vo = new WeChatPublicAccountPushVO(); vo.setTouser(openId); - vo.setTemplateId(config.getWxTemplateId()); - vo.setData(data); - if (StringUtils.isNotEmpty(config.getRedirectUrl())) { - vo.setUrl(config.getRedirectUrl()); + vo.setTemplateId(msgParams.getTemplateId()); + vo.setData(sendMap); + if (StringUtils.isNotEmpty(msgParams.getRedirectUrl())) { + vo.setUrl(msgParams.getRedirectUrl()); } try { NotifySendResponse resp = doPost(JSON.toJSONString(vo), pushUrl); log.info("[Push] configId={} sn={} userId={} level={} status={}", - config.getId(), serialNumber, userEntry.getKey(), level.getLabel(), resp.getStatus()); + config.getId(), serialNumber, userEntry.getKey(), + level.getLabel(), resp.getStatus()); } catch (Exception e) { log.error("[Push] configId={} sn={} userId={} 推送异常: {}", config.getId(), serialNumber, userEntry.getKey(), e.getMessage()); @@ -201,13 +230,25 @@ public class ThingsModelPushHandler { } } + private List extractVarNames(String content) { + List names = new ArrayList<>(); + if (StringUtils.isEmpty(content)) { + return names; + } + Matcher matcher = VAR_PATTERN.matcher(content); + while (matcher.find()) { + names.add(matcher.group(1).replace(".DATA", "")); + } + return names; + } + private NotifySendResponse doPost(String json, String url) { NotifySendResponse resp = new NotifySendResponse(); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.parseMediaType("application/json; charset=UTF-8")); headers.add("Accept", MediaType.APPLICATION_JSON.toString()); - ResponseEntity result = REST_TEMPLATE.postForEntity(url, - new HttpEntity<>(json, headers), String.class); + ResponseEntity result = REST_TEMPLATE.postForEntity( + url, new HttpEntity<>(json, headers), String.class); String body = result.getBody(); resp.setResultContent(body); try { @@ -217,28 +258,4 @@ public class ThingsModelPushHandler { } return resp; } - - /** - * 确定推送涉及的设备编号列表 - * device_id=0 → 产品级,需查产品下所有设备 - * device_id>0 → 指定设备 - */ - private List resolveSerialNumbers(ThingsModelPushConfig config) { - if (config.getDeviceId() != null && config.getDeviceId() > 0) { - if (StringUtils.isNotEmpty(config.getSerialNumber())) { - return Collections.singletonList(config.getSerialNumber().toUpperCase()); - } - return Collections.emptyList(); - } - // 产品级:从 Redis 中找该产品所有已有 TSLV 缓存的设备(通过前缀扫描) - String pattern = FastBeeConstant.REDIS.DEVICE_PRE_KEY + config.getProductId() + "_*"; - Collection keys = redisCache.keys(pattern); - if (keys == null || keys.isEmpty()) { - return Collections.emptyList(); - } - String prefix = FastBeeConstant.REDIS.DEVICE_PRE_KEY + config.getProductId() + "_"; - return keys.stream() - .map(k -> k.substring(prefix.length())) - .collect(Collectors.toList()); - } } diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/ThingsModelPushConfig.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/ThingsModelPushConfig.java index cff47bf0..8d5fbb84 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/ThingsModelPushConfig.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/ThingsModelPushConfig.java @@ -31,26 +31,26 @@ public class ThingsModelPushConfig extends BaseEntity { @ApiModelProperty("产品ID") private Long productId; - @ApiModelProperty("产品名称(非持久化字段)") + @ApiModelProperty("产品名称(非持久化,JOIN查询)") private String productName; - @ApiModelProperty("设备ID,0=产品级") + @ApiModelProperty("设备ID(必须指定具体设备)") private Long deviceId; + @ApiModelProperty("设备名称(非持久化,JOIN查询)") + private String deviceName; + + @ApiModelProperty("设备类型:1=直连 2=网关(非持久化,JOIN查询)") + private Integer deviceType; + @ApiModelProperty("设备编号") private String serialNumber; - @ApiModelProperty("通知渠道ID") - private Long channelId; + @ApiModelProperty("通知模板ID(关联 notify_template)") + private Long templateId; - @ApiModelProperty("通知渠道名称(非持久化字段)") - private String channelName; - - @ApiModelProperty("微信公众号模板ID") - private String wxTemplateId; - - @ApiModelProperty("消息点击跳转URL") - private String redirectUrl; + @ApiModelProperty("通知模板名称(非持久化,JOIN查询)") + private String templateName; @ApiModelProperty("推送间隔(分钟),0=仅手动推送") private Integer pushInterval; diff --git a/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/ThingsModelPushConfigMapper.xml b/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/ThingsModelPushConfigMapper.xml index 963be1e7..84d84876 100644 --- a/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/ThingsModelPushConfigMapper.xml +++ b/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/ThingsModelPushConfigMapper.xml @@ -4,28 +4,28 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - @@ -52,36 +52,38 @@ c.id, c.config_name, c.product_id, c.device_id, c.serial_number, - c.channel_id, c.wx_template_id, c.redirect_url, - c.push_interval, c.last_push_time, c.status, + c.template_id, c.push_interval, c.last_push_time, c.status, c.tenant_id, c.tenant_name, c.create_by, c.create_time, c.update_by, c.update_time, - p.product_name, nc.name AS channel_name + p.product_name, p.device_type, d.device_name, nt.name AS template_name + + + + FROM iot_things_model_push_config c + LEFT JOIN iot_device d ON c.device_id = d.device_id + LEFT JOIN iot_product p ON c.product_id = p.product_id + LEFT JOIN notify_template nt ON c.template_id = nt.id SELECT - FROM iot_things_model_push_config c - LEFT JOIN iot_product p ON c.product_id = p.product_id - LEFT JOIN notify_channel nc ON c.channel_id = nc.id + WHERE c.del_flag = '0' AND c.status = 1 AND c.push_interval > 0 INSERT INTO iot_things_model_push_config - (config_name, product_id, device_id, serial_number, channel_id, - wx_template_id, redirect_url, push_interval, status, + (config_name, product_id, device_id, serial_number, template_id, + push_interval, status, tenant_id, tenant_name, create_by, create_time, del_flag) VALUES - (#{configName}, #{productId}, #{deviceId}, #{serialNumber}, #{channelId}, - #{wxTemplateId}, #{redirectUrl}, #{pushInterval}, #{status}, + (#{configName}, #{productId}, #{deviceId}, #{serialNumber}, #{templateId}, + #{pushInterval}, #{status}, #{tenantId}, #{tenantName}, #{createBy}, #{createTime}, '0') @@ -127,9 +124,7 @@ product_id = #{productId}, device_id = #{deviceId}, serial_number = #{serialNumber}, - channel_id = #{channelId}, - wx_template_id = #{wxTemplateId}, - redirect_url = #{redirectUrl}, + template_id = #{templateId}, push_interval = #{pushInterval}, status = #{status}, update_by = #{updateBy}, diff --git a/sql/iot_things_model_push.sql b/sql/iot_things_model_push.sql index 9f5ac9b2..53398505 100644 --- a/sql/iot_things_model_push.sql +++ b/sql/iot_things_model_push.sql @@ -7,11 +7,9 @@ CREATE TABLE IF NOT EXISTS `iot_things_model_push_config` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', `config_name` VARCHAR(128) NOT NULL COMMENT '配置名称', `product_id` BIGINT NOT NULL COMMENT '产品ID', - `device_id` BIGINT DEFAULT 0 COMMENT '设备ID,0=产品级(产品下所有设备)', - `serial_number` VARCHAR(64) DEFAULT NULL COMMENT '设备编号(device_id>0时有效)', - `channel_id` BIGINT NOT NULL COMMENT '通知渠道ID (notify_channel.id,类型为 public_account)', - `wx_template_id` VARCHAR(128) NOT NULL COMMENT '微信公众号模板ID(在微信公众平台配置)', - `redirect_url` VARCHAR(512) DEFAULT NULL COMMENT '消息点击跳转URL', + `device_id` BIGINT DEFAULT NULL COMMENT '设备ID(必须指定具体设备)', + `serial_number` VARCHAR(64) DEFAULT NULL COMMENT '设备编号', + `template_id` BIGINT NOT NULL COMMENT '通知模板ID(关联 notify_template.id)', `push_interval` INT DEFAULT 60 COMMENT '推送间隔(分钟),0=仅手动推送', `last_push_time` DATETIME DEFAULT NULL COMMENT '最近一次推送时间', `status` TINYINT DEFAULT 1 COMMENT '状态:0=停用 1=启用', @@ -23,8 +21,8 @@ CREATE TABLE IF NOT EXISTS `iot_things_model_push_config` ( `update_time` DATETIME DEFAULT NULL COMMENT '更新时间', `del_flag` CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', PRIMARY KEY (`id`), - KEY `idx_product_id` (`product_id`), - KEY `idx_device_id` (`device_id`) + KEY `idx_device_id` (`device_id`), + KEY `idx_template_id` (`template_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物模型数据推送配置'; @@ -59,11 +57,9 @@ CREATE TABLE IF NOT EXISTS `iot_push_user` ( -- sys_menu 菜单权限(挂在"规则引擎"父菜单 parent_id=3051 下) -- ============================================================ --- 主菜单:数据推送配置 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 (3430, '数据推送配置', 3051, 9, 'pushConfig', 'iot/push/index', NULL, 1, 0, 'C', '0', '0', 'iot:push:list', 'notification', 'admin', now(), 'admin', now(), '微信服务号物模型数据推送'); --- 按钮权限 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 (3431, '推送配置查询', 3430, 1, '#', '', NULL, 1, 0, 'F', '0', '0', 'iot:push:query', '#', 'admin', now(), 'admin', now(), ''); @@ -78,3 +74,17 @@ 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) 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`; diff --git a/yack-web/src/views/iot/push/index.vue b/yack-web/src/views/iot/push/index.vue index 9e403b8e..a09182a5 100644 --- a/yack-web/src/views/iot/push/index.vue +++ b/yack-web/src/views/iot/push/index.vue @@ -2,14 +2,15 @@

- + - + - - - - + + @@ -27,15 +28,21 @@ - 新增 + 新增 - - - + + + +