From b7e09846549d0c67bcc419df47daf416beb8134e Mon Sep 17 00:00:00 2001 From: zhugongping Date: Thu, 26 Mar 2026 22:59:16 +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 | 84 ++++ .../framework/config/SchedulingConfig.java | 2 + .../iot/data/push/ThingsModelPushHandler.java | 244 +++++++++++ .../iot/data/push/ThingsModelPushJob.java | 67 +++ .../service/impl/WeChatPushServiceImpl.java | 1 + .../notify/core/wechat/vo/TemplateDataVo.java | 18 +- .../push/ThingsModelPushController.java | 114 ++++++ .../com/fastbee/iot/domain/PushModelItem.java | 48 +++ .../java/com/fastbee/iot/domain/PushUser.java | 27 ++ .../iot/domain/ThingsModelPushConfig.java | 76 ++++ .../mapper/ThingsModelPushConfigMapper.java | 57 +++ .../IThingsModelPushConfigService.java | 32 ++ .../ThingsModelPushConfigServiceImpl.java | 110 +++++ .../iot/ThingsModelPushConfigMapper.xml | 205 ++++++++++ sql/iot_things_model_push.sql | 80 ++++ yack-web/src/api/iot/pushConfig.js | 38 ++ yack-web/src/views/iot/push/index.vue | 384 ++++++++++++++++++ 17 files changed, 1582 insertions(+), 5 deletions(-) create mode 100644 fastbee-common/src/main/java/com/fastbee/common/enums/DataLevelEnum.java create mode 100644 fastbee-iot-data/src/main/java/com/fastbee/iot/data/push/ThingsModelPushHandler.java create mode 100644 fastbee-iot-data/src/main/java/com/fastbee/iot/data/push/ThingsModelPushJob.java create mode 100644 fastbee-open-api/src/main/java/com/fastbee/controller/push/ThingsModelPushController.java create mode 100644 fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/PushModelItem.java create mode 100644 fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/PushUser.java create mode 100644 fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/ThingsModelPushConfig.java create mode 100644 fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/mapper/ThingsModelPushConfigMapper.java create mode 100644 fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/IThingsModelPushConfigService.java create mode 100644 fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/ThingsModelPushConfigServiceImpl.java create mode 100644 fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/ThingsModelPushConfigMapper.xml create mode 100644 sql/iot_things_model_push.sql create mode 100644 yack-web/src/api/iot/pushConfig.js create mode 100644 yack-web/src/views/iot/push/index.vue 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 new file mode 100644 index 00000000..2274cd88 --- /dev/null +++ b/fastbee-common/src/main/java/com/fastbee/common/enums/DataLevelEnum.java @@ -0,0 +1,84 @@ +package com.fastbee.common.enums; + +/** + * 物模型数据等级枚举 + * 绿色=正常,黄色=告警,红色=警报 + * + * @author fastbee + */ +public enum DataLevelEnum { + + GREEN(1, "✅ 正常", "#009900"), + YELLOW(2, "⚠️ 告警", "#FF9900"), + RED(3, "🔴 警报", "#FF0000"); + + private final int code; + private final String label; + /** 微信模板消息字体颜色 */ + private final String color; + + DataLevelEnum(int code, String label, String color) { + this.code = code; + this.label = label; + this.color = color; + } + + public int getCode() { + return code; + } + + public String getLabel() { + return label; + } + + public String getColor() { + return color; + } + + /** + * 根据值和阈值配置计算数据等级 + * + * @param value 物模型上报值(字符串) + * @param greenMin 绿色下限(null=不限) + * @param greenMax 绿色上限(null=不限) + * @param yellowMin 黄色下限(null=不限) + * @param yellowMax 黄色上限(null=不限) + * @return 数据等级 + */ + public static DataLevelEnum evaluate(String value, + String greenMin, String greenMax, + String yellowMin, String yellowMax) { + if (value == null) { + return RED; + } + // 优先尝试数值比较 + try { + double v = Double.parseDouble(value.trim()); + if (inNumericRange(v, greenMin, greenMax)) { + return GREEN; + } + if (inNumericRange(v, yellowMin, yellowMax)) { + return YELLOW; + } + return RED; + } catch (NumberFormatException e) { + // 字符串等值比较 + if (value.equals(greenMin)) { + return GREEN; + } + if (value.equals(yellowMin)) { + return YELLOW; + } + return RED; + } + } + + private static boolean inNumericRange(double v, String min, String max) { + if (min == null && max == null) { + return false; + } + boolean lowerOk = (min == null) || v >= Double.parseDouble(min); + boolean upperOk = (max == null) || v <= Double.parseDouble(max); + return lowerOk && upperOk; + } +} diff --git a/fastbee-framework/src/main/java/com/fastbee/framework/config/SchedulingConfig.java b/fastbee-framework/src/main/java/com/fastbee/framework/config/SchedulingConfig.java index cb1183b6..95fd8892 100644 --- a/fastbee-framework/src/main/java/com/fastbee/framework/config/SchedulingConfig.java +++ b/fastbee-framework/src/main/java/com/fastbee/framework/config/SchedulingConfig.java @@ -1,6 +1,7 @@ package com.fastbee.framework.config; import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.config.ScheduledTaskRegistrar; @@ -12,6 +13,7 @@ import java.util.concurrent.ScheduledExecutorService; * - 使用项目自定义的 scheduledExecutorService(已做 MDC 透传与异常打印) */ @Configuration +@EnableScheduling public class SchedulingConfig implements SchedulingConfigurer { @Resource(name = "scheduledExecutorService") 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 new file mode 100644 index 00000000..9e66b46c --- /dev/null +++ b/fastbee-iot-data/src/main/java/com/fastbee/iot/data/push/ThingsModelPushHandler.java @@ -0,0 +1,244 @@ +package com.fastbee.iot.data.push; + +import com.alibaba.fastjson2.JSON; +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.redis.RedisCache; +import com.fastbee.common.core.redis.RedisKeyBuilder; +import com.fastbee.common.enums.DataLevelEnum; +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.ThingsModelPushConfig; +import com.fastbee.iot.model.ThingsModels.ValueItem; +import com.fastbee.iot.service.IThingsModelPushConfigService; +import com.fastbee.notify.domain.NotifyChannel; +import com.fastbee.notify.core.wechat.vo.TemplateDataVo; +import com.fastbee.notify.core.wechat.vo.WeChatPublicAccountPushVO; +import com.fastbee.notify.service.INotifyChannelService; +import com.fastbee.iot.service.ISocialUserService; +import com.fastbee.iot.domain.SocialUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +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.stream.Collectors; + +/** + * 物模型数据推送核心处理器 + * 读取 Redis 中设备最新上报值,按配置的阈值评估数据等级, + * 同等级数据合并为一条微信服务号模板消息推送。 + * + * @author fastbee + */ +@Slf4j +@Component +public class ThingsModelPushHandler { + + private static final RestTemplate REST_TEMPLATE = new RestTemplate(); + + @Resource + private IThingsModelPushConfigService pushConfigService; + @Resource + private INotifyChannelService notifyChannelService; + @Resource + private ISocialUserService socialUserService; + @Resource + 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; + } + + // 获取通知渠道(公众号) + NotifyChannel channel = notifyChannelService.selectNotifyChannelById(config.getChannelId()); + if (channel == null) { + log.warn("[Push] configId={} 渠道不存在 channelId={}", config.getId(), config.getChannelId()); + return; + } + WeChatConfigParams wxConfig = JSONObject.parseObject(channel.getConfigContent(), WeChatConfigParams.class); + if (StringUtils.isEmpty(wxConfig.getAppId()) || StringUtils.isEmpty(wxConfig.getAppSecret())) { + log.warn("[Push] configId={} 渠道微信配置不完整", config.getId()); + return; + } + + // 获取 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()); + return; + } + 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 列表 + List userIds = config.getUserIds(); + if (CollectionUtils.isEmpty(userIds)) { + log.warn("[Push] configId={} 没有配置接收用户,跳过", config.getId()); + return; + } + List socialUsers = socialUserService.listWechatPublicAccountOpenId( + userIds.stream().map(Object::toString).collect(Collectors.toList())); + Map openIdMap = socialUsers.stream() + .collect(Collectors.toMap(SocialUser::getSysUserId, SocialUser::getOpenId, (a, b) -> b)); + + // 对每台设备推送 + for (String serialNumber : serialNumbers) { + pushForDevice(config, serialNumber, pushUrl, openIdMap); + } + + // 更新最近推送时间 + pushConfigService.updateLastPushTime(config.getId()); + } + + /** + * 对单台设备执行推送 + */ + private void pushForDevice(ThingsModelPushConfig config, String serialNumber, + String pushUrl, Map openIdMap) { + String redisKey = RedisKeyBuilder.buildTSLVCacheKey(config.getProductId(), serialNumber); + + // 按等级分组收集显示文本 + Map> levelTextMap = new LinkedHashMap<>(); + for (DataLevelEnum level : DataLevelEnum.values()) { + levelTextMap.put(level, new ArrayList<>()); + } + + for (PushModelItem item : config.getItems()) { + String cacheValue = redisCache.getCacheMapValue(redisKey, item.getIdentifier()); + if (StringUtils.isEmpty(cacheValue)) { + continue; + } + String value; + try { + ValueItem vi = JSON.parseObject(cacheValue, ValueItem.class); + value = vi.getValue(); + } catch (Exception e) { + value = cacheValue; + } + DataLevelEnum level = DataLevelEnum.evaluate( + 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() : ""); + 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); + } + } + + /** + * 发送单条微信模板消息(带颜色标记) + */ + private void sendTemplateMsg(ThingsModelPushConfig config, 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")); + + 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()); + } + 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()); + } catch (Exception e) { + log.error("[Push] configId={} sn={} userId={} 推送异常: {}", + config.getId(), serialNumber, userEntry.getKey(), e.getMessage()); + } + } + } + + 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); + String body = result.getBody(); + resp.setResultContent(body); + try { + resp.setStatus("0".equals(JSONObject.parseObject(body).get("errcode").toString()) ? 1 : 0); + } catch (Exception e) { + resp.setStatus(0); + } + 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-iot-data/src/main/java/com/fastbee/iot/data/push/ThingsModelPushJob.java b/fastbee-iot-data/src/main/java/com/fastbee/iot/data/push/ThingsModelPushJob.java new file mode 100644 index 00000000..e729e95f --- /dev/null +++ b/fastbee-iot-data/src/main/java/com/fastbee/iot/data/push/ThingsModelPushJob.java @@ -0,0 +1,67 @@ +package com.fastbee.iot.data.push; + +import com.fastbee.iot.domain.ThingsModelPushConfig; +import com.fastbee.iot.service.IThingsModelPushConfigService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Date; +import java.util.List; + +/** + * 物模型数据定时推送调度器 + * 每分钟检查所有启用的推送配置,按照 push_interval 决定是否触发本次推送。 + * + * @author fastbee + */ +@Slf4j +@Component +public class ThingsModelPushJob { + + @Resource + private IThingsModelPushConfigService pushConfigService; + @Resource + private ThingsModelPushHandler pushHandler; + + /** + * 每分钟执行一次,检查并触发到期的推送任务 + */ + @Scheduled(fixedDelay = 60_000) + public void execute() { + List configs = pushConfigService.selectAllEnabled(); + if (CollectionUtils.isEmpty(configs)) { + return; + } + long now = System.currentTimeMillis(); + for (ThingsModelPushConfig config : configs) { + try { + if (isDue(config, now)) { + log.info("[PushJob] 触发推送 configId={} configName={}", config.getId(), config.getConfigName()); + pushHandler.doPush(config); + } + } catch (Exception e) { + log.error("[PushJob] 推送异常 configId={}: {}", config.getId(), e.getMessage(), e); + } + } + } + + /** + * 判断配置是否到达推送时间 + * push_interval=0 时不自动推送(仅手动) + */ + private boolean isDue(ThingsModelPushConfig config, long nowMs) { + if (config.getPushInterval() == null || config.getPushInterval() <= 0) { + return false; + } + Date lastPush = config.getLastPushTime(); + if (lastPush == null) { + // 从未推送过,立即触发 + return true; + } + long intervalMs = (long) config.getPushInterval() * 60 * 1000; + return (nowMs - lastPush.getTime()) >= intervalMs; + } +} diff --git a/fastbee-notify/fastbee-notify-core/src/main/java/com/fastbee/notify/core/wechat/service/impl/WeChatPushServiceImpl.java b/fastbee-notify/fastbee-notify-core/src/main/java/com/fastbee/notify/core/wechat/service/impl/WeChatPushServiceImpl.java index 5cda4791..c7d37dd4 100644 --- a/fastbee-notify/fastbee-notify-core/src/main/java/com/fastbee/notify/core/wechat/service/impl/WeChatPushServiceImpl.java +++ b/fastbee-notify/fastbee-notify-core/src/main/java/com/fastbee/notify/core/wechat/service/impl/WeChatPushServiceImpl.java @@ -71,6 +71,7 @@ public class WeChatPushServiceImpl implements WeChatPushService { break; case WECHAT_PUBLIC_ACCOUNT: notifySendResponse = this.weChatPublicAccountSend(notifyVO); + break; default: break; } diff --git a/fastbee-notify/fastbee-notify-core/src/main/java/com/fastbee/notify/core/wechat/vo/TemplateDataVo.java b/fastbee-notify/fastbee-notify-core/src/main/java/com/fastbee/notify/core/wechat/vo/TemplateDataVo.java index 80459f5e..b6c79f3a 100644 --- a/fastbee-notify/fastbee-notify-core/src/main/java/com/fastbee/notify/core/wechat/vo/TemplateDataVo.java +++ b/fastbee-notify/fastbee-notify-core/src/main/java/com/fastbee/notify/core/wechat/vo/TemplateDataVo.java @@ -1,19 +1,27 @@ package com.fastbee.notify.core.wechat.vo; import com.alibaba.fastjson.annotation.JSONField; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor -@AllArgsConstructor public class TemplateDataVo { - /*微信文档中要求的格式 "data": { "name01": {"value": "某某"},"thing01": {"value": "广州至北京" - } ,"date01": {"value": "2018-01-01"} - }*/ + /*微信文档中要求的格式 "data": { "name01": {"value": "某某","color":"#173177"},"thing01": {"value": "广州至北京"} }*/ @JSONField(name = "value") private String value; + /** 字体颜色,格式 #RRGGBB,为 null 时不序列化(微信默认色) */ + @JSONField(name = "color") + private String color; + + public TemplateDataVo(String value) { + this.value = value; + } + + public TemplateDataVo(String value, String color) { + this.value = value; + this.color = color; + } } diff --git a/fastbee-open-api/src/main/java/com/fastbee/controller/push/ThingsModelPushController.java b/fastbee-open-api/src/main/java/com/fastbee/controller/push/ThingsModelPushController.java new file mode 100644 index 00000000..6645fd66 --- /dev/null +++ b/fastbee-open-api/src/main/java/com/fastbee/controller/push/ThingsModelPushController.java @@ -0,0 +1,114 @@ +package com.fastbee.controller.push; + +import com.fastbee.common.annotation.Log; +import com.fastbee.common.core.controller.BaseController; +import com.fastbee.common.core.domain.AjaxResult; +import com.fastbee.common.core.page.TableDataInfo; +import com.fastbee.common.enums.BusinessType; +import com.fastbee.common.utils.SecurityUtils; +import com.fastbee.iot.data.push.ThingsModelPushHandler; +import com.fastbee.iot.domain.ThingsModelPushConfig; +import com.fastbee.iot.service.IThingsModelPushConfigService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 物模型数据推送配置 Controller + * + * @author fastbee + */ +@Api(tags = "物模型数据推送配置") +@RestController +@RequestMapping("/iot/push/config") +public class ThingsModelPushController extends BaseController { + + @Resource + private IThingsModelPushConfigService pushConfigService; + @Resource + private ThingsModelPushHandler pushHandler; + + /** 查询列表 */ + @ApiOperation("查询推送配置列表") + @PreAuthorize("@ss.hasPermi('iot:push:list')") + @GetMapping("/list") + public TableDataInfo list(ThingsModelPushConfig query) { + startPage(); + List list = pushConfigService.selectList(query); + return getDataTable(list); + } + + /** 获取详情 */ + @ApiOperation("获取推送配置详情") + @PreAuthorize("@ss.hasPermi('iot:push:query')") + @GetMapping("/{id}") + public AjaxResult getById(@PathVariable Long id) { + return AjaxResult.success(pushConfigService.selectById(id)); + } + + /** 新增 */ + @ApiOperation("新增推送配置") + @PreAuthorize("@ss.hasPermi('iot:push:add')") + @Log(title = "推送配置", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody @Validated ThingsModelPushConfig config) { + config.setCreateBy(SecurityUtils.getUsername()); + return toAjax(pushConfigService.insertConfig(config)); + } + + /** 修改 */ + @ApiOperation("修改推送配置") + @PreAuthorize("@ss.hasPermi('iot:push:edit')") + @Log(title = "推送配置", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody ThingsModelPushConfig config) { + config.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(pushConfigService.updateConfig(config)); + } + + /** 启用/停用 */ + @ApiOperation("修改推送配置状态") + @PreAuthorize("@ss.hasPermi('iot:push:edit')") + @Log(title = "推送配置状态", businessType = BusinessType.UPDATE) + @PutMapping("/status") + public AjaxResult changeStatus(@RequestBody ThingsModelPushConfig config) { + ThingsModelPushConfig update = new ThingsModelPushConfig(); + update.setId(config.getId()); + update.setStatus(config.getStatus()); + update.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(pushConfigService.updateConfig(update)); + } + + /** 删除 */ + @ApiOperation("删除推送配置") + @PreAuthorize("@ss.hasPermi('iot:push:remove')") + @Log(title = "推送配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) { + return toAjax(pushConfigService.deleteByIds(ids)); + } + + /** + * 手动立即推送最新数据 + */ + @ApiOperation("手动立即推送最新设备数据") + @PreAuthorize("@ss.hasPermi('iot:push:push')") + @Log(title = "手动推送", businessType = BusinessType.OTHER) + @PostMapping("/manual/{id}") + public AjaxResult manualPush(@PathVariable Long id) { + ThingsModelPushConfig config = pushConfigService.selectById(id); + if (config == null) { + return AjaxResult.error("推送配置不存在"); + } + if (config.getStatus() == null || config.getStatus() == 0) { + return AjaxResult.error("推送配置已停用,请先启用"); + } + pushHandler.doPush(config); + return AjaxResult.success("推送已触发"); + } +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/PushModelItem.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/PushModelItem.java new file mode 100644 index 00000000..f8ae6a9b --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/PushModelItem.java @@ -0,0 +1,48 @@ +package com.fastbee.iot.domain; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 推送物模型项及等级阈值配置 iot_push_model_item + * + * @author sumuxi + */ +@Data +@ApiModel(value = "PushModelItem", description = "推送物模型项及等级阈值配置") +public class PushModelItem { + + @ApiModelProperty("主键") + private Long id; + + @ApiModelProperty("推送配置ID") + private Long configId; + + @ApiModelProperty("物模型ID") + private Long modelId; + + @ApiModelProperty("物模型标识符") + private String identifier; + + @ApiModelProperty("物模型名称") + private String modelName; + + @ApiModelProperty("单位") + private String unit; + + @ApiModelProperty("绿色(正常)下限,null=不限") + private String greenMin; + + @ApiModelProperty("绿色(正常)上限,null=不限") + private String greenMax; + + @ApiModelProperty("黄色(告警)下限,null=不限") + private String yellowMin; + + @ApiModelProperty("黄色(告警)上限,null=不限") + private String yellowMax; + + @ApiModelProperty("排列顺序") + private Integer sortOrder; +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/PushUser.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/PushUser.java new file mode 100644 index 00000000..f56ebcb7 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/PushUser.java @@ -0,0 +1,27 @@ +package com.fastbee.iot.domain; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 推送接收用户 iot_push_user + * + * @author sumuxi + */ +@Data +@ApiModel(value = "PushUser", description = "推送接收用户") +public class PushUser { + + @ApiModelProperty("主键") + private Long id; + + @ApiModelProperty("推送配置ID") + private Long configId; + + @ApiModelProperty("用户ID") + private Long userId; + + @ApiModelProperty("用户名") + private String userName; +} 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 new file mode 100644 index 00000000..cff47bf0 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/ThingsModelPushConfig.java @@ -0,0 +1,76 @@ +package com.fastbee.iot.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fastbee.common.core.domain.BaseEntity; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; +import java.util.List; + +/** + * 物模型数据推送配置 iot_things_model_push_config + * + * @author fastbee + */ +@Data +@EqualsAndHashCode(callSuper = true) +@ApiModel(value = "ThingsModelPushConfig", description = "物模型数据推送配置") +public class ThingsModelPushConfig extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("主键") + private Long id; + + @ApiModelProperty("配置名称") + private String configName; + + @ApiModelProperty("产品ID") + private Long productId; + + @ApiModelProperty("产品名称(非持久化字段)") + private String productName; + + @ApiModelProperty("设备ID,0=产品级") + private Long deviceId; + + @ApiModelProperty("设备编号") + private String serialNumber; + + @ApiModelProperty("通知渠道ID") + private Long channelId; + + @ApiModelProperty("通知渠道名称(非持久化字段)") + private String channelName; + + @ApiModelProperty("微信公众号模板ID") + private String wxTemplateId; + + @ApiModelProperty("消息点击跳转URL") + private String redirectUrl; + + @ApiModelProperty("推送间隔(分钟),0=仅手动推送") + private Integer pushInterval; + + @ApiModelProperty("最近推送时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date lastPushTime; + + @ApiModelProperty("状态:0=停用 1=启用") + private Integer status; + + @ApiModelProperty("租户ID") + private Long tenantId; + + @ApiModelProperty("租户名称") + private String tenantName; + + /** 物模型项列表(非持久化,前端用) */ + private List items; + + /** 接收用户列表(非持久化,前端用) */ + private List userIds; +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/mapper/ThingsModelPushConfigMapper.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/mapper/ThingsModelPushConfigMapper.java new file mode 100644 index 00000000..0e5076e2 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/mapper/ThingsModelPushConfigMapper.java @@ -0,0 +1,57 @@ +package com.fastbee.iot.mapper; + +import com.fastbee.iot.domain.PushModelItem; +import com.fastbee.iot.domain.PushUser; +import com.fastbee.iot.domain.ThingsModelPushConfig; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; + +import java.util.Date; +import java.util.List; + +/** + * 物模型数据推送配置 Mapper + * + * @author fastbee + */ +@Repository +public interface ThingsModelPushConfigMapper { + + ThingsModelPushConfig selectById(Long id); + + List selectList(ThingsModelPushConfig query); + + /** 查询所有启用的推送配置(含物模型项和接收用户) */ + List selectAllEnabled(); + + /** 按产品ID+设备ID查询启用配置 */ + List selectEnabledByProductAndDevice(@Param("productId") Long productId, + @Param("deviceId") Long deviceId); + + int insert(ThingsModelPushConfig config); + + int update(ThingsModelPushConfig config); + + int deleteById(Long id); + + int deleteByIds(Long[] ids); + + /** 更新最近推送时间 */ + int updateLastPushTime(@Param("id") Long id, @Param("lastPushTime") Date lastPushTime); + + // ---- 物模型项 ---- + + List selectItemsByConfigId(Long configId); + + int insertItems(List items); + + int deleteItemsByConfigId(Long configId); + + // ---- 接收用户 ---- + + List selectUsersByConfigId(Long configId); + + int insertUsers(List users); + + int deleteUsersByConfigId(Long configId); +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/IThingsModelPushConfigService.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/IThingsModelPushConfigService.java new file mode 100644 index 00000000..aea5a798 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/IThingsModelPushConfigService.java @@ -0,0 +1,32 @@ +package com.fastbee.iot.service; + +import com.fastbee.iot.domain.ThingsModelPushConfig; + +import java.util.List; + +/** + * 物模型数据推送配置 Service 接口 + * + * @author sumuxi + */ +public interface IThingsModelPushConfigService { + + ThingsModelPushConfig selectById(Long id); + + List selectList(ThingsModelPushConfig query); + + /** 查询所有启用且设置了推送间隔的配置(用于定时任务) */ + List selectAllEnabled(); + + /** 按产品/设备查询启用配置 */ + List selectEnabledByProductAndDevice(Long productId, Long deviceId); + + int insertConfig(ThingsModelPushConfig config); + + int updateConfig(ThingsModelPushConfig config); + + int deleteByIds(Long[] ids); + + /** 更新最近推送时间 */ + void updateLastPushTime(Long id); +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/ThingsModelPushConfigServiceImpl.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/ThingsModelPushConfigServiceImpl.java new file mode 100644 index 00000000..8da36cd2 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/ThingsModelPushConfigServiceImpl.java @@ -0,0 +1,110 @@ +package com.fastbee.iot.service.impl; + +import com.fastbee.common.utils.DateUtils; +import com.fastbee.iot.domain.PushModelItem; +import com.fastbee.iot.domain.PushUser; +import com.fastbee.iot.domain.ThingsModelPushConfig; +import com.fastbee.iot.mapper.ThingsModelPushConfigMapper; +import com.fastbee.iot.service.IThingsModelPushConfigService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 物模型数据推送配置 Service 实现 + * + * @author fastbee + */ +@Slf4j +@Service +public class ThingsModelPushConfigServiceImpl implements IThingsModelPushConfigService { + + @Resource + private ThingsModelPushConfigMapper mapper; + + @Override + public ThingsModelPushConfig selectById(Long id) { + return mapper.selectById(id); + } + + @Override + public List selectList(ThingsModelPushConfig query) { + return mapper.selectList(query); + } + + @Override + public List selectAllEnabled() { + return mapper.selectAllEnabled(); + } + + @Override + public List selectEnabledByProductAndDevice(Long productId, Long deviceId) { + return mapper.selectEnabledByProductAndDevice(productId, deviceId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int insertConfig(ThingsModelPushConfig config) { + config.setCreateTime(DateUtils.getNowDate()); + config.setStatus(config.getStatus() == null ? 1 : config.getStatus()); + config.setDeviceId(config.getDeviceId() == null ? 0L : config.getDeviceId()); + config.setPushInterval(config.getPushInterval() == null ? 60 : config.getPushInterval()); + int rows = mapper.insert(config); + saveItemsAndUsers(config); + return rows; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int updateConfig(ThingsModelPushConfig config) { + config.setUpdateTime(DateUtils.getNowDate()); + int rows = mapper.update(config); + mapper.deleteItemsByConfigId(config.getId()); + mapper.deleteUsersByConfigId(config.getId()); + saveItemsAndUsers(config); + return rows; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteByIds(Long[] ids) { + for (Long id : ids) { + mapper.deleteItemsByConfigId(id); + mapper.deleteUsersByConfigId(id); + } + return mapper.deleteByIds(ids); + } + + @Override + public void updateLastPushTime(Long id) { + mapper.updateLastPushTime(id, new Date()); + } + + private void saveItemsAndUsers(ThingsModelPushConfig config) { + if (CollectionUtils.isNotEmpty(config.getItems())) { + List items = config.getItems(); + items.forEach(item -> { + item.setConfigId(config.getId()); + if (item.getSortOrder() == null) { + item.setSortOrder(0); + } + }); + mapper.insertItems(items); + } + if (CollectionUtils.isNotEmpty(config.getUserIds())) { + List users = config.getUserIds().stream().map(uid -> { + PushUser u = new PushUser(); + u.setConfigId(config.getId()); + u.setUserId(uid); + return u; + }).collect(Collectors.toList()); + mapper.insertUsers(users); + } + } +} 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 new file mode 100644 index 00000000..963be1e7 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/ThingsModelPushConfigMapper.xml @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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.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 + + + + + + + + + + + + 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, + tenant_id, tenant_name, create_by, create_time, del_flag) + VALUES + (#{configName}, #{productId}, #{deviceId}, #{serialNumber}, #{channelId}, + #{wxTemplateId}, #{redirectUrl}, #{pushInterval}, #{status}, + #{tenantId}, #{tenantName}, #{createBy}, #{createTime}, '0') + + + + UPDATE iot_things_model_push_config + + config_name = #{configName}, + product_id = #{productId}, + device_id = #{deviceId}, + serial_number = #{serialNumber}, + channel_id = #{channelId}, + wx_template_id = #{wxTemplateId}, + redirect_url = #{redirectUrl}, + push_interval = #{pushInterval}, + status = #{status}, + update_by = #{updateBy}, + update_time = #{updateTime} + + WHERE id = #{id} + + + + UPDATE iot_things_model_push_config SET del_flag = '2' WHERE id = #{id} + + + + UPDATE iot_things_model_push_config SET del_flag = '2' + WHERE id IN + + #{id} + + + + + UPDATE iot_things_model_push_config SET last_push_time = #{lastPushTime} WHERE id = #{id} + + + + + + + INSERT INTO iot_push_model_item + (config_id, model_id, identifier, model_name, unit, + green_min, green_max, yellow_min, yellow_max, sort_order) + VALUES + + (#{item.configId}, #{item.modelId}, #{item.identifier}, #{item.modelName}, #{item.unit}, + #{item.greenMin}, #{item.greenMax}, #{item.yellowMin}, #{item.yellowMax}, #{item.sortOrder}) + + + + + DELETE FROM iot_push_model_item WHERE config_id = #{configId} + + + + + + + + + INSERT INTO iot_push_user (config_id, user_id, user_name) + VALUES + + (#{u.configId}, #{u.userId}, #{u.userName}) + + + + + DELETE FROM iot_push_user WHERE config_id = #{configId} + + + diff --git a/sql/iot_things_model_push.sql b/sql/iot_things_model_push.sql new file mode 100644 index 00000000..9f5ac9b2 --- /dev/null +++ b/sql/iot_things_model_push.sql @@ -0,0 +1,80 @@ +-- ============================================================ +-- 物模型数据推送配置 (微信服务号模板消息) +-- 执行顺序:在主库 fastbee 中执行 +-- ============================================================ + +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', + `push_interval` INT DEFAULT 60 COMMENT '推送间隔(分钟),0=仅手动推送', + `last_push_time` DATETIME DEFAULT NULL COMMENT '最近一次推送时间', + `status` TINYINT DEFAULT 1 COMMENT '状态:0=停用 1=启用', + `tenant_id` BIGINT DEFAULT NULL COMMENT '租户ID', + `tenant_name` VARCHAR(64) DEFAULT NULL COMMENT '租户名称', + `create_by` VARCHAR(64) DEFAULT NULL COMMENT '创建者', + `create_time` DATETIME DEFAULT NULL COMMENT '创建时间', + `update_by` VARCHAR(64) DEFAULT NULL COMMENT '更新者', + `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`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物模型数据推送配置'; + + +CREATE TABLE IF NOT EXISTS `iot_push_model_item` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', + `config_id` BIGINT NOT NULL COMMENT '推送配置ID', + `model_id` BIGINT DEFAULT NULL COMMENT '物模型ID(iot_things_model.model_id)', + `identifier` VARCHAR(128) NOT NULL COMMENT '物模型标识符', + `model_name` VARCHAR(128) DEFAULT NULL COMMENT '物模型名称(冗余展示用)', + `unit` VARCHAR(32) DEFAULT NULL COMMENT '单位(冗余展示用)', + `green_min` VARCHAR(64) DEFAULT NULL COMMENT '绿色(正常)下限,NULL表示不限', + `green_max` VARCHAR(64) DEFAULT NULL COMMENT '绿色(正常)上限,NULL表示不限', + `yellow_min` VARCHAR(64) DEFAULT NULL COMMENT '黄色(告警)下限,NULL表示不限', + `yellow_max` VARCHAR(64) DEFAULT NULL COMMENT '黄色(告警)上限,NULL表示不限', + `sort_order` INT DEFAULT 0 COMMENT '排列顺序', + PRIMARY KEY (`id`), + KEY `idx_config_id` (`config_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='推送物模型项及等级阈值配置'; + + +CREATE TABLE IF NOT EXISTS `iot_push_user` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', + `config_id` BIGINT NOT NULL COMMENT '推送配置ID', + `user_id` BIGINT NOT NULL COMMENT '系统用户ID(需已绑定微信公众号)', + `user_name` VARCHAR(64) DEFAULT NULL COMMENT '用户名(冗余展示用)', + PRIMARY KEY (`id`), + KEY `idx_config_id` (`config_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='推送接收用户'; + + +-- ============================================================ +-- 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(), ''); + +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 (3432, '推送配置新增', 3430, 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'iot:push:add', '#', '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 (3433, '推送配置修改', 3430, 3, '#', '', NULL, 1, 0, 'F', '0', '0', 'iot:push:edit', '#', '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 (3434, '推送配置删除', 3430, 4, '#', '', NULL, 1, 0, 'F', '0', '0', 'iot:push:remove', '#', '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 (3435, '手动立即推送', 3430, 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'iot:push:push', '#', 'admin', now(), 'admin', now(), ''); diff --git a/yack-web/src/api/iot/pushConfig.js b/yack-web/src/api/iot/pushConfig.js new file mode 100644 index 00000000..c2ee2d3b --- /dev/null +++ b/yack-web/src/api/iot/pushConfig.js @@ -0,0 +1,38 @@ +import request from '@/utils/request'; + +const BASE = '/iot/push/config'; + +// 查询推送配置列表 +export function listPushConfig(query) { + return request({ url: `${BASE}/list`, method: 'get', params: query }); +} + +// 获取推送配置详情 +export function getPushConfig(id) { + return request({ url: `${BASE}/${id}`, method: 'get' }); +} + +// 新增推送配置 +export function addPushConfig(data) { + return request({ url: BASE, method: 'post', data }); +} + +// 修改推送配置 +export function updatePushConfig(data) { + return request({ url: BASE, method: 'put', data }); +} + +// 修改状态 +export function changePushConfigStatus(id, status) { + return request({ url: `${BASE}/status`, method: 'put', data: { id, status } }); +} + +// 删除推送配置 +export function delPushConfig(ids) { + return request({ url: `${BASE}/${ids}`, method: 'delete' }); +} + +// 手动立即推送最新数据 +export function manualPush(id) { + return request({ url: `${BASE}/manual/${id}`, method: 'post' }); +} diff --git a/yack-web/src/views/iot/push/index.vue b/yack-web/src/views/iot/push/index.vue new file mode 100644 index 00000000..9e403b8e --- /dev/null +++ b/yack-web/src/views/iot/push/index.vue @@ -0,0 +1,384 @@ + + + + +