20260328:数据推送:最推送最新数据

This commit is contained in:
George 2026-03-28 20:44:42 +08:00
parent c25f288b6c
commit 8a16a35627
3 changed files with 67 additions and 39 deletions

View File

@ -6,12 +6,12 @@ import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.notify.NotifySendResponse; import com.fastbee.common.core.notify.NotifySendResponse;
import com.fastbee.common.core.notify.config.WeChatConfigParams; import com.fastbee.common.core.notify.config.WeChatConfigParams;
import com.fastbee.common.core.notify.msg.WechatMsgParams; 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.core.redis.RedisKeyBuilder;
import com.fastbee.common.enums.DataLevelEnum; import com.fastbee.common.enums.DataLevelEnum;
import com.fastbee.common.utils.StringUtils; import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.wechat.WechatUtils; import com.fastbee.common.utils.wechat.WechatUtils;
import com.fastbee.common.wechat.WeChatAppResult; import com.fastbee.common.wechat.WeChatAppResult;
import com.fastbee.iot.cache.ITSLCache;
import com.fastbee.iot.domain.PushModelItem; import com.fastbee.iot.domain.PushModelItem;
import com.fastbee.iot.domain.SocialUser; import com.fastbee.iot.domain.SocialUser;
import com.fastbee.iot.domain.ThingsModelPushConfig; import com.fastbee.iot.domain.ThingsModelPushConfig;
@ -64,7 +64,7 @@ public class ThingsModelPushHandler {
@Resource @Resource
private ISocialUserService socialUserService; private ISocialUserService socialUserService;
@Resource @Resource
private RedisCache redisCache; private ITSLCache itslCache;
/** /**
* 按推送配置执行推送 * 按推送配置执行推送
@ -129,16 +129,16 @@ public class ThingsModelPushHandler {
} }
// ---------- 执行推送 ---------- // ---------- 执行推送 ----------
pushForDevice(config, msgParams, pushUrl, openIdMap); if (pushForDevice(config, msgParams, pushUrl, openIdMap)) {
// 更新最近推送时间 // 更新最近推送时间
pushConfigService.updateLastPushTime(config.getId()); pushConfigService.updateLastPushTime(config.getId());
} }
}
/** /**
* 对单台设备按等级分组推送 * 对单台设备按等级分组推送
*/ */
private void pushForDevice(ThingsModelPushConfig config, WechatMsgParams msgParams, private boolean pushForDevice(ThingsModelPushConfig config, WechatMsgParams msgParams,
String pushUrl, Map<Long, String> openIdMap) { String pushUrl, Map<Long, String> openIdMap) {
String serialNumber = config.getSerialNumber().toUpperCase(); String serialNumber = config.getSerialNumber().toUpperCase();
String redisKey = RedisKeyBuilder.buildTSLVCacheKey(config.getProductId(), serialNumber); String redisKey = RedisKeyBuilder.buildTSLVCacheKey(config.getProductId(), serialNumber);
@ -147,17 +147,18 @@ public class ThingsModelPushHandler {
for (DataLevelEnum lvl : DataLevelEnum.values()) { for (DataLevelEnum lvl : DataLevelEnum.values()) {
levelTextMap.put(lvl, new ArrayList<>()); levelTextMap.put(lvl, new ArrayList<>());
} }
Map<String, ValueItem> cacheMap = itslCache.getSimpleThingsModelValueItemByCache(redisKey);
for (PushModelItem item : config.getItems()) { for (PushModelItem item : config.getItems()) {
String cacheValue = redisCache.getCacheMapValue(redisKey, item.getIdentifier()); ValueItem valueItem = cacheMap.get(item.getIdentifier());
if (StringUtils.isEmpty(cacheValue)) { if (valueItem == null) {
continue; continue;
} }
String value; String value = valueItem.getValue();
try { Date ts = valueItem.getTs();
value = JSON.parseObject(cacheValue, ValueItem.class).getValue(); if (config.getLastPushTime() != null && ts != null && ts.before(config.getLastPushTime())) {
} catch (Exception e) { log.warn("[Push] configId={} sn={} item={} value={} 时间{}早于上次推送时间{},跳过",
value = cacheValue; config.getId(), serialNumber, item.getIdentifier(), value, ts, config.getLastPushTime());
return false;
} }
// 没有值 直接跳过 // 没有值 直接跳过
@ -184,13 +185,14 @@ public class ThingsModelPushHandler {
if (chosen == null) { if (chosen == null) {
log.debug("[Push] configId={} sn={} 无有效分级数据,跳过发送", log.debug("[Push] configId={} sn={} 无有效分级数据,跳过发送",
config.getId(), serialNumber); config.getId(), serialNumber);
return; return false;
} }
String dataText = String.join(";", levelTextMap.get(chosen)); String dataText = String.join(";", levelTextMap.get(chosen));
if (dataText.length() > 32) { if (dataText.length() > 32) {
dataText = dataText.substring(0, 29) + "..."; dataText = dataText.substring(0, 29) + "...";
} }
sendTemplateMsg(config, msgParams, serialNumber, chosen, dataText, pushUrl, openIdMap);
return sendTemplateMsg(config, msgParams, serialNumber, chosen, dataText, pushUrl, openIdMap);
} }
/** /**
@ -198,7 +200,7 @@ public class ThingsModelPushHandler {
* 从通知模板的 content 字段提取 {{变量.DATA}} 变量名顺序, * 从通知模板的 content 字段提取 {{变量.DATA}} 变量名顺序,
* 按顺序映射:第1个→设备名称,第2个→数据等级,第3个→监测数据。 * 按顺序映射:第1个→设备名称,第2个→数据等级,第3个→监测数据。
*/ */
private void sendTemplateMsg(ThingsModelPushConfig config, WechatMsgParams msgParams, private boolean sendTemplateMsg(ThingsModelPushConfig config, WechatMsgParams msgParams,
String serialNumber, DataLevelEnum level, String dataText, String serialNumber, DataLevelEnum level, String dataText,
String pushUrl, Map<Long, String> openIdMap) { String pushUrl, Map<Long, String> openIdMap) {
List<String> varNames = extractVarNames(msgParams.getContent()); List<String> varNames = extractVarNames(msgParams.getContent());
@ -227,6 +229,7 @@ public class ThingsModelPushHandler {
sendMap.put(m.getKey(), new TemplateDataVo(m.getValue())); sendMap.put(m.getKey(), new TemplateDataVo(m.getValue()));
} }
NotifySendResponse resp = null;
for (Map.Entry<Long, String> userEntry : openIdMap.entrySet()) { for (Map.Entry<Long, String> userEntry : openIdMap.entrySet()) {
String openId = userEntry.getValue(); String openId = userEntry.getValue();
WeChatPublicAccountPushVO vo = new WeChatPublicAccountPushVO(); WeChatPublicAccountPushVO vo = new WeChatPublicAccountPushVO();
@ -237,15 +240,13 @@ public class ThingsModelPushHandler {
vo.setUrl(msgParams.getRedirectUrl() + "pagesA/home/device/index?deviceId=" + config.getDeviceId()); vo.setUrl(msgParams.getRedirectUrl() + "pagesA/home/device/index?deviceId=" + config.getDeviceId());
} }
try { try {
NotifySendResponse resp = doPost(JSON.toJSONString(vo), pushUrl); 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) { } catch (Exception e) {
log.error("[Push] configId={} sn={} userId={} 推送异常: {}", log.error("[Push] configId={} sn={} userId={} 推送异常: {}",
config.getId(), serialNumber, userEntry.getKey(), e.getMessage()); config.getId(), serialNumber, userEntry.getKey(), e.getMessage());
} }
} }
return Objects.nonNull(resp) && resp.getStatus() == 1;
} }
private List<String> extractVarNames(String content) { private List<String> extractVarNames(String content) {

View File

@ -1,6 +1,7 @@
package com.fastbee.iot.cache; package com.fastbee.iot.cache;
import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem; import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem;
import com.fastbee.iot.model.ThingsModels.ValueItem;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -135,4 +136,11 @@ public interface ITSLCache {
*/ */
ThingsModelValueItem getSingleDeviceCalculatedModel(String serialNumber, String identifier); ThingsModelValueItem getSingleDeviceCalculatedModel(String serialNumber, String identifier);
/**
* 根据redisKey获取简易物模型缓存值
*
* @param redisKey
* @return
*/
Map<String, ValueItem> getSimpleThingsModelValueItemByCache(String redisKey);
} }

View File

@ -16,12 +16,14 @@ import com.fastbee.iot.model.ThingsModelItem.Datatype;
import com.fastbee.iot.model.ThingsModels.IdentifierVO; import com.fastbee.iot.model.ThingsModels.IdentifierVO;
import com.fastbee.iot.model.ThingsModels.PropertyDto; import com.fastbee.iot.model.ThingsModels.PropertyDto;
import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem; import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem;
import com.fastbee.iot.model.ThingsModels.ValueItem;
import com.fastbee.iot.service.IModbusConfigService; import com.fastbee.iot.service.IModbusConfigService;
import com.fastbee.iot.service.IThingsModelService; import com.fastbee.iot.service.IThingsModelService;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import javax.annotation.Nullable;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
@ -183,14 +185,8 @@ public class TSLCacheImpl implements ITSLCache {
assert !Objects.isNull(productId) : "产品id为空"; assert !Objects.isNull(productId) : "产品id为空";
// redis获取物模型 // redis获取物模型
Map<String, Object> map = redisCache.getCacheMap(RedisKeyBuilder.buildTSLCacheKey(productId)); Map<String, Object> map = redisCache.getCacheMap(RedisKeyBuilder.buildTSLCacheKey(productId));
if (!CollectionUtils.isEmpty(map)) { Map<String, ThingsModelValueItem> itemMap = buildThingsModelValueItemMap(map);
Map<String, ThingsModelValueItem> itemMap = map.values().stream().map(v -> JSON.parseObject(v.toString(), ThingsModelValueItem.class)).collect( if (itemMap != null) return itemMap;
Collectors.toMap(ThingsModelValueItem::getId, Function.identity()));
for (ThingsModelValueItem item : itemMap.values()) {
convertModelName(item, SecurityUtils.getLanguage());
}
return itemMap;
}
return setCacheThMapByProductId(productId); return setCacheThMapByProductId(productId);
} }
@ -345,15 +341,8 @@ public class TSLCacheImpl implements ITSLCache {
} }
String cacheKey = RedisKeyBuilder.buildDeviceTSLCacheKey(serialNumber); String cacheKey = RedisKeyBuilder.buildDeviceTSLCacheKey(serialNumber);
Map<String, Object> map = redisCache.getCacheMap(cacheKey); Map<String, Object> map = redisCache.getCacheMap(cacheKey);
if (!CollectionUtils.isEmpty(map)) { Map<String, ThingsModelValueItem> itemMap = buildThingsModelValueItemMap(map);
Map<String, ThingsModelValueItem> itemMap = map.values().stream() if (itemMap != null) return itemMap;
.map(v -> JSON.parseObject(v.toString(), ThingsModelValueItem.class))
.collect(Collectors.toMap(ThingsModelValueItem::getId, Function.identity()));
for (ThingsModelValueItem item : itemMap.values()) {
convertModelName(item, SecurityUtils.getLanguage());
}
return itemMap;
}
// 缓存未命中,从数据库加载 // 缓存未命中,从数据库加载
return refreshDeviceCalculatedModelCache(serialNumber); return refreshDeviceCalculatedModelCache(serialNumber);
} }
@ -449,4 +438,34 @@ public class TSLCacheImpl implements ITSLCache {
return item; return item;
} }
@Override
public Map<String, ValueItem> getSimpleThingsModelValueItemByCache(String redisKey) {
Map<String, Object> map = redisCache.getCacheMap(redisKey);
return buildValueItemMap(map);
}
@Nullable
private Map<String, ThingsModelValueItem> buildThingsModelValueItemMap(Map<String, Object> map) {
if (!CollectionUtils.isEmpty(map)) {
Map<String, ThingsModelValueItem> itemMap = map.values().stream()
.map(v -> JSON.parseObject(v.toString(), ThingsModelValueItem.class))
.collect(Collectors.toMap(ThingsModelValueItem::getId, Function.identity()));
for (ThingsModelValueItem item : itemMap.values()) {
convertModelName(item, SecurityUtils.getLanguage());
}
return itemMap;
}
return null;
}
@Nullable
private Map<String, ValueItem> buildValueItemMap(Map<String, Object> map) {
if (!CollectionUtils.isEmpty(map)) {
return map.values().stream()
.map(v -> JSON.parseObject(v.toString(), ValueItem.class))
.collect(Collectors.toMap(ValueItem::getId, Function.identity()));
}
return null;
}
} }