news:合并2.0商业版代码:完整模块

版本更新汇总 (2025-08-10 至 2025-09-23)
核心功能优化:
- 新增UDP模块并默认开启,TCP基础上增加UDP适配
- 支持网关绑定多产品子设备,优化设备重连机制
- 新增Ezviz(萤石)接入模块,调整imoulife支持自有平台账号

性能与稳定性:
- 优化设备影子热数据存储,增加强制刷盘功能
- 产品列表及MQTT查询增加多级缓存策略
- 强化TCP安全机制,新增异常攻击封禁策略

功能完善:
- 集成rulego可视化规则引擎基础框架
- Web-IDE新增20+ modbus/hex封装方法
- 优化乐城播放器界面及TCP连接信息展示

问题修复:
- 解决系统间强依赖及注入相关问题
- 修复时间格式化、设备创建时间显示问题
- 完善日志处理及应用推送相关异常

其他改进:
- 重构TCP粘包处理,删除冗余代码
- 增加Docker部署配置
- 细化权限控制策略
This commit is contained in:
奈科斯(NexIoT)
2025-09-23 11:11:17 +08:00
parent ce3d4f50bb
commit eb3a8dab68
211 changed files with 7087 additions and 10885 deletions
@@ -0,0 +1,147 @@
package cn.universal.admin.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* 连接配置管理
* 复用现有配置,减少重复配置项
*/
@Data
@Component
public class ConnectionConfig {
// TCP配置 - 复用现有配置
@Value("${tcp.protocol.sni.host.out:#productKey.tcp.nexiot.xyz}")
private String tcpSniHostTemplate;
@Value("${tcp.protocol.sni.port.out:38883}")
private Integer tcpSniPort;
@Value("${connection.tcp.connection-type:TLS SNI方式}")
private String tcpConnectionType;
@Value("${connection.tcp.description:}")
private String tcpDescription;
// MQTT配置 - 复用现有配置
@Value("${mqtt.cfg.host.out:tcp://localhost:1883}")
private String mqttHostOut;
@Value("${mqtt.cfg.host:tcp://localhost:1883}")
private String mqttHost;
@Value("${mqtt.cfg.clientIdPrefix:univ_iot_test}")
private String mqttClientIdPrefix;
@Value("${connection.mqtt.connection-type:内置MQTT Broker}")
private String mqttConnectionType;
@Value("${connection.mqtt.description:}")
private String mqttDescription;
// MQTT主题配置 - 直接在代码中定义默认值,避免Spring占位符解析问题
@Value("${connection.mqtt.default-topics.thing-topics.property-up:}")
private String mqttThingPropertyUp;
@Value("${connection.mqtt.default-topics.thing-topics.event-up:}")
private String mqttThingEventUp;
@Value("${connection.mqtt.default-topics.thing-topics.command-down:}")
private String mqttThingCommandDown;
@Value("${connection.mqtt.default-topics.passthrough-topics.data-up:}")
private String mqttPassthroughDataUp;
@Value("${connection.mqtt.default-topics.passthrough-topics.command-down:}")
private String mqttPassthroughCommandDown;
// 默认主题模板常量
private static final String DEFAULT_THING_PROPERTY_UP = "$thing/up/property/{productKey}/{deviceId}";
private static final String DEFAULT_THING_EVENT_UP = "$thing/up/event/{productKey}/{deviceId}";
private static final String DEFAULT_THING_COMMAND_DOWN = "$thing/down/{productKey}/{deviceId}";
private static final String DEFAULT_PASSTHROUGH_DATA_UP = "$thing/up/{productKey}/{deviceId}";
private static final String DEFAULT_PASSTHROUGH_COMMAND_DOWN = "$thing/down/{productKey}/{deviceId}";
// UDP配置
@Value("${connection.udp.connection-type:}")
private String udpConnectionType;
@Value("${connection.udp.description:}")
private String udpDescription;
@Value("${udp.cfg.port:1884}")
private Integer udpPort;
/**
* 获取TCP主机地址
*/
public String getTcpHost(String productKey) {
return tcpSniHostTemplate.replace("#productKey", productKey);
}
/**
* 获取MQTT主机地址(优先使用外部配置)
*/
public String getMqttHost() {
return mqttHostOut != null && !mqttHostOut.isEmpty() ? mqttHostOut : mqttHost;
}
/**
* 从主机地址中提取端口
*/
public Integer extractPortFromHost(String host) {
if (host == null || host.isEmpty()) {
return null;
}
if (host.contains(":")) {
String[] parts = host.split(":");
if (parts.length >= 2) {
try {
return Integer.parseInt(parts[parts.length - 1]);
} catch (NumberFormatException e) {
// 忽略解析错误
}
}
}
return null;
}
/**
* 获取MQTT物模型属性上报主题
*/
public String getMqttThingPropertyUp() {
return mqttThingPropertyUp.isEmpty() ? DEFAULT_THING_PROPERTY_UP : mqttThingPropertyUp;
}
/**
* 获取MQTT物模型事件上报主题
*/
public String getMqttThingEventUp() {
return mqttThingEventUp.isEmpty() ? DEFAULT_THING_EVENT_UP : mqttThingEventUp;
}
/**
* 获取MQTT物模型指令下发主题
*/
public String getMqttThingCommandDown() {
return mqttThingCommandDown.isEmpty() ? DEFAULT_THING_COMMAND_DOWN : mqttThingCommandDown;
}
/**
* 获取MQTT透传数据上报主题
*/
public String getMqttPassthroughDataUp() {
return mqttPassthroughDataUp.isEmpty() ? DEFAULT_PASSTHROUGH_DATA_UP : mqttPassthroughDataUp;
}
/**
* 获取MQTT透传指令下发主题
*/
public String getMqttPassthroughCommandDown() {
return mqttPassthroughCommandDown.isEmpty() ? DEFAULT_PASSTHROUGH_COMMAND_DOWN : mqttPassthroughCommandDown;
}
}
@@ -0,0 +1,27 @@
package cn.universal.admin.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* 安全配置
* 管理敏感字段的加密密钥
*/
@Data
@Component
public class SecurityConfig {
/**
* 产品密钥加密密钥
* 建议在生产环境中使用环境变量或配置中心
*/
@Value("${security.product-secret.key:cn-universal-product-secret-key-2024}")
private String productSecretKey;
/**
* 是否启用敏感字段加密
*/
@Value("${security.sensitive-field.enabled:true}")
private Boolean sensitiveFieldEnabled;
}
@@ -0,0 +1,114 @@
package cn.universal.admin.platform.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/** 连接信息数据传输对象 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ConnectionInfoDTO {
/** 内置连接信息 */
private BuiltinConnection builtin;
/** 网络组件连接信息 */
private NetworkConnection network;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class BuiltinConnection {
/** 连接方式 */
private String connectionType;
/** 连接地址 */
private String host;
/** 端口 */
private Integer port;
/** 用户名 */
private String username;
/** 密码(默认不显示,需要点击查看) */
private String password;
/** 配置说明 */
private String description;
/** MQTT主题信息(仅MQTT类型有) */
private TopicInfo topics;
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class NetworkConnection {
/** 是否启用网络组件 */
private Boolean enabled;
/** 组件名称 */
private String name;
/** 组件类型 */
private String type;
/** 连接地址 */
private String host;
/** 端口 */
private Integer port;
/** 用户名 */
private String username;
/** 密码(默认不显示,需要点击查看) */
private String password;
/** 运行状态 */
private Boolean state;
/** 订阅主题信息 */
private TopicInfo subscribeTopics;
/** 错误信息(当enabled为false时) */
private String message;
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class TopicInfo {
/** 物模型主题 */
private ThingTopics thingTopics;
/** 透传主题 */
private PassthroughTopics passthroughTopics;
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class ThingTopics {
private String propertyUp;
private String eventUp;
private String commandDown;
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class PassthroughTopics {
private String dataUp;
private String commandDown;
}
}
@@ -0,0 +1,23 @@
package cn.universal.admin.platform.service;
import cn.universal.admin.platform.dto.ConnectionInfoDTO;
/**
* 连接信息服务接口
*/
public interface ConnectionInfoService {
/**
* 获取产品的连接信息
* @param productKey 产品Key
* @return 连接信息
*/
ConnectionInfoDTO getConnectionInfo(String productKey);
/**
* 获取产品的MQTT密码信息
* @param productKey 产品Key
* @return 密码信息
*/
ConnectionInfoDTO getMqttPasswordInfo(String productKey);
}
@@ -0,0 +1,272 @@
package cn.universal.admin.platform.service.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.universal.admin.config.ConnectionConfig;
import cn.universal.admin.platform.dto.ConnectionInfoDTO;
import cn.universal.admin.platform.service.ConnectionInfoService;
import cn.universal.persistence.entity.IoTProduct;
import cn.universal.persistence.entity.Network;
import cn.universal.persistence.mapper.IoTProductMapper;
import cn.universal.persistence.mapper.NetworkMapper;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/** 连接信息服务实现 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ConnectionInfoServiceImpl implements ConnectionInfoService {
private final IoTProductMapper productMapper;
private final NetworkMapper networkMapper;
private final ConnectionConfig connectionConfig;
@Override
public ConnectionInfoDTO getConnectionInfo(String productKey) {
log.debug("获取产品连接信息: productKey={}", productKey);
// 查询产品信息
IoTProduct product = productMapper.getProductByProductKey(productKey);
if (product == null) {
throw new RuntimeException("产品不存在: " + productKey);
}
// 构建内置连接信息
ConnectionInfoDTO.BuiltinConnection builtin = buildBuiltinConnection(product);
// 构建网络组件连接信息
ConnectionInfoDTO.NetworkConnection network = buildNetworkConnection(product);
return ConnectionInfoDTO.builder().builtin(builtin).network(network).build();
}
@Override
public ConnectionInfoDTO getMqttPasswordInfo(String productKey) {
log.debug("获取产品MQTT密码信息: productKey={}", productKey);
// 查询产品信息
IoTProduct product = productMapper.getProductByProductKey(productKey);
if (product == null) {
throw new RuntimeException("产品不存在: " + productKey);
}
// 构建内置MQTT密码信息
ConnectionInfoDTO.BuiltinConnection builtin = buildBuiltinMqttPassword(product);
// 构建网络组件MQTT密码信息
ConnectionInfoDTO.NetworkConnection network = buildNetworkMqttPassword(product);
return ConnectionInfoDTO.builder().builtin(builtin).network(network).build();
}
/** 构建内置连接信息 */
private ConnectionInfoDTO.BuiltinConnection buildBuiltinConnection(IoTProduct product) {
String platform = product.getThirdPlatform();
ConnectionInfoDTO.BuiltinConnection.BuiltinConnectionBuilder builder =
ConnectionInfoDTO.BuiltinConnection.builder();
switch (platform) {
case "tcp":
return builder
.connectionType(connectionConfig.getTcpConnectionType())
.host(connectionConfig.getTcpHost(product.getProductKey()))
.port(connectionConfig.getTcpSniPort())
.description(connectionConfig.getTcpDescription())
.build();
case "mqtt":
ConnectionInfoDTO.BuiltinConnection builtinMqtt =
builder
.connectionType(connectionConfig.getMqttConnectionType())
.host(connectionConfig.getMqttHost())
.port(connectionConfig.extractPortFromHost(connectionConfig.getMqttHost()))
.username(product.getProductKey())
.password("点击查看密码")
.description(connectionConfig.getMqttDescription())
.topics(buildDefaultTopics(product.getProductKey()))
.build();
return builtinMqtt;
case "udp":
return builder
.connectionType(connectionConfig.getUdpConnectionType())
.host(connectionConfig.getTcpHost(product.getProductKey()))
.port(connectionConfig.getUdpPort())
.description(connectionConfig.getUdpDescription())
.build();
default:
return builder
.connectionType("请联系管理员")
.host("请联系管理员")
.port(0)
.description("暂无")
.build();
}
}
/** 构建网络组件连接信息 */
private ConnectionInfoDTO.NetworkConnection buildNetworkConnection(IoTProduct product) {
if (StrUtil.isBlank(product.getNetworkUnionId())) {
return ConnectionInfoDTO.NetworkConnection.builder()
.enabled(false)
.message("未绑定网络组件")
.build();
}
// 查询网络组件
List<Network> networks = networkMapper.selectByUnionId(product.getNetworkUnionId());
if (networks.isEmpty()) {
return ConnectionInfoDTO.NetworkConnection.builder()
.enabled(false)
.message("绑定的网络组件不存在")
.build();
}
Network network = networks.get(0);
JSONObject config = JSONUtil.parseObj(network.getConfiguration());
ConnectionInfoDTO.NetworkConnection.NetworkConnectionBuilder builder =
ConnectionInfoDTO.NetworkConnection.builder()
.enabled(true)
.name(network.getName())
.type(network.getType())
.state(network.getState())
.host(config.getStr("host"))
.port(config.getInt("port"))
.username(config.getStr("username"))
.password("点击查看密码");
// 处理MQTT主题信息
if ("mqtt".equals(product.getThirdPlatform())) {
ConnectionInfoDTO.TopicInfo subscribeTopics =
buildSubscribeTopics(config, product.getProductKey());
builder.subscribeTopics(subscribeTopics);
}
return builder.build();
}
/** 构建内置MQTT密码信息 */
private ConnectionInfoDTO.BuiltinConnection buildBuiltinMqttPassword(IoTProduct product) {
if (!"mqtt".equals(product.getThirdPlatform())) {
return null;
}
return ConnectionInfoDTO.BuiltinConnection.builder()
.connectionType(connectionConfig.getMqttConnectionType())
.host(connectionConfig.getMqttHost())
.port(connectionConfig.extractPortFromHost(connectionConfig.getMqttHost()))
.username(product.getProductKey())
.password(product.getProductSecret())
.description(connectionConfig.getMqttDescription())
.build();
}
/** 构建网络组件MQTT密码信息 */
private ConnectionInfoDTO.NetworkConnection buildNetworkMqttPassword(IoTProduct product) {
if (StrUtil.isBlank(product.getNetworkUnionId())) {
return ConnectionInfoDTO.NetworkConnection.builder()
.enabled(false)
.message("未绑定网络组件")
.build();
}
List<Network> networks = networkMapper.selectByUnionId(product.getNetworkUnionId());
if (networks.isEmpty()) {
return ConnectionInfoDTO.NetworkConnection.builder()
.enabled(false)
.message("绑定的网络组件不存在")
.build();
}
Network network = networks.get(0);
JSONObject config = JSONUtil.parseObj(network.getConfiguration());
return ConnectionInfoDTO.NetworkConnection.builder()
.enabled(true)
.name(network.getName())
.type(network.getType())
.host(config.getStr("host"))
.port(config.getInt("port"))
.username(config.getStr("username"))
.password(config.getStr("password"))
.build();
}
/** 构建默认主题信息 */
private ConnectionInfoDTO.TopicInfo buildDefaultTopics(String productKey) {
ConnectionInfoDTO.ThingTopics thingTopics =
ConnectionInfoDTO.ThingTopics.builder()
.propertyUp(replaceProductKey(connectionConfig.getMqttThingPropertyUp(), productKey))
.eventUp(replaceProductKey(connectionConfig.getMqttThingEventUp(), productKey))
.commandDown(replaceProductKey(connectionConfig.getMqttThingCommandDown(), productKey))
.build();
ConnectionInfoDTO.PassthroughTopics passthroughTopics =
ConnectionInfoDTO.PassthroughTopics.builder()
.dataUp(replaceProductKey(connectionConfig.getMqttPassthroughDataUp(), productKey))
.commandDown(
replaceProductKey(connectionConfig.getMqttPassthroughCommandDown(), productKey))
.build();
return ConnectionInfoDTO.TopicInfo.builder()
.thingTopics(thingTopics)
.passthroughTopics(passthroughTopics)
.build();
}
/** 构建订阅主题信息 */
private ConnectionInfoDTO.TopicInfo buildSubscribeTopics(JSONObject config, String productKey) {
JSONObject subscribeTopics = config.getJSONObject("subscribeTopics");
if (subscribeTopics == null || subscribeTopics.isEmpty()) {
// 使用默认主题
return buildDefaultTopics(productKey);
}
ConnectionInfoDTO.ThingTopics thingTopics = null;
ConnectionInfoDTO.PassthroughTopics passthroughTopics = null;
JSONObject thingTopicsConfig = subscribeTopics.getJSONObject("thingTopics");
if (thingTopicsConfig != null) {
thingTopics =
ConnectionInfoDTO.ThingTopics.builder()
.propertyUp(thingTopicsConfig.getStr("propertyUp"))
.eventUp(thingTopicsConfig.getStr("eventUp"))
.commandDown(thingTopicsConfig.getStr("commandDown"))
.build();
}
JSONObject passthroughTopicsConfig = subscribeTopics.getJSONObject("passthroughTopics");
if (passthroughTopicsConfig != null) {
passthroughTopics =
ConnectionInfoDTO.PassthroughTopics.builder()
.dataUp(passthroughTopicsConfig.getStr("dataUp"))
.commandDown(passthroughTopicsConfig.getStr("commandDown"))
.build();
}
return ConnectionInfoDTO.TopicInfo.builder()
.thingTopics(thingTopics)
.passthroughTopics(passthroughTopics)
.build();
}
/** 替换主题中的产品Key占位符 */
private String replaceProductKey(String topic, String productKey) {
if (StrUtil.isBlank(topic)) {
return topic;
}
// 支持两种格式:{productKey} 和 ${productKey}
return topic
.replace("{productKey}", productKey)
.replace("${productKey}", productKey)
.replace("{deviceId}", "${deviceId}")
.replace("${deviceId}", "${deviceId}");
}
}
@@ -0,0 +1,143 @@
package cn.universal.admin.system.notice;
import cn.hutool.json.JSONObject;
import cn.universal.admin.common.utils.SecurityUtils;
import cn.universal.admin.system.web.BaseController;
import cn.universal.manager.notice.dto.NoticeChannelDTO;
import cn.universal.manager.notice.model.NoticeChannel;
import cn.universal.manager.notice.service.NoticeChannelService;
import cn.universal.manager.notice.util.JsonDesensitizationUtil;
import cn.universal.persistence.page.TableDataInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/admin/v1/notice/channel")
public class NoticeChannelController extends BaseController {
private final NoticeChannelService noticeChannelService;
private final ObjectMapper objectMapper;
@Autowired
public NoticeChannelController(
NoticeChannelService noticeChannelService, ObjectMapper objectMapper) {
this.noticeChannelService = noticeChannelService;
this.objectMapper = objectMapper;
}
@GetMapping("/list")
public TableDataInfo<NoticeChannelDTO> list(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String name,
@RequestParam(required = false) String channelType) {
startPage();
List<NoticeChannel> list = noticeChannelService.search(name, channelType, null);
List<NoticeChannelDTO> dtoList =
list.stream()
.map(
channel -> {
NoticeChannelDTO dto = new NoticeChannelDTO();
dto.setId(channel.getId());
dto.setName(channel.getName());
dto.setChannelType(channel.getChannelType());
dto.setStatus(channel.getStatus());
dto.setRemark(channel.getRemark());
dto.setCreator(channel.getCreator());
dto.setCreateTime(channel.getCreateTime());
dto.setUpdateTime(channel.getUpdateTime());
// 将config字符串转换为对象并进行脱敏处理
if (channel.getConfig() != null && !channel.getConfig().isEmpty()) {
try {
JSONObject desensitizedConfig =
JsonDesensitizationUtil.desensitize(channel.getConfig());
dto.setConfig(desensitizedConfig);
} catch (Exception e) {
dto.setConfig(null);
}
}
return dto;
})
.toList();
return getDataTable(dtoList);
}
@GetMapping("/get")
public NoticeChannelDTO get(@RequestParam Long id) {
try {
NoticeChannel channel = noticeChannelService.getById(id);
if (channel == null) {
return null;
}
NoticeChannelDTO dto = new NoticeChannelDTO();
dto.setId(channel.getId());
dto.setName(channel.getName());
dto.setChannelType(channel.getChannelType());
dto.setStatus(channel.getStatus());
dto.setRemark(channel.getRemark());
dto.setCreator(channel.getCreator());
dto.setCreateTime(channel.getCreateTime());
dto.setUpdateTime(channel.getUpdateTime());
// 将config字符串转换为对象并进行脱敏处理
if (channel.getConfig() != null && !channel.getConfig().isEmpty()) {
JSONObject desensitizedConfig = JsonDesensitizationUtil.desensitize(channel.getConfig());
dto.setConfig(desensitizedConfig);
}
return dto;
} catch (Exception e) {
return null;
}
}
@PostMapping("/save")
public Map<String, Object> save(@RequestBody NoticeChannelDTO channelDTO) {
try {
NoticeChannel channel = new NoticeChannel();
channel.setId(channelDTO.getId());
channel.setName(channelDTO.getName());
channel.setChannelType(channelDTO.getChannelType());
channel.setStatus(channelDTO.getStatus());
channel.setRemark(channelDTO.getRemark());
channel.setCreator(SecurityUtils.getUnionId());
channel.setCreateTime(channelDTO.getCreateTime());
channel.setUpdateTime(channelDTO.getUpdateTime());
// 将config对象转换为JSON字符串
if (channelDTO.getConfig() != null) {
channel.setConfig(objectMapper.writeValueAsString(channelDTO.getConfig()));
}
noticeChannelService.save(channel);
return Map.of("code", 0, "msg", "操作成功");
} catch (Exception e) {
return Map.of("code", 1, "msg", "操作失败: " + e.getMessage());
}
}
@PostMapping("/delete")
public Map<String, Object> delete(@RequestBody List<Long> ids) {
try {
if (ids != null && !ids.isEmpty()) {
noticeChannelService.deleteBatch(ids);
return Map.of("code", 0, "msg", "删除成功");
} else {
return Map.of("code", 1, "msg", "未提供有效的ID");
}
} catch (Exception e) {
return Map.of("code", 1, "msg", "删除失败: " + e.getMessage());
}
}
}
@@ -0,0 +1,35 @@
package cn.universal.admin.system.notice;
import cn.universal.admin.system.web.BaseController;
import cn.universal.manager.notice.model.NoticeSendRecord;
import cn.universal.manager.notice.service.NoticeRecordService;
import cn.universal.persistence.page.TableDataInfo;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/** 通知记录 */
@RestController
@RequestMapping("/admin/v1/notice/record")
public class NoticeRecordController extends BaseController {
private final NoticeRecordService recordService;
@Autowired
public NoticeRecordController(NoticeRecordService recordService) {
this.recordService = recordService;
}
@GetMapping("/page")
public TableDataInfo<NoticeSendRecord> page(
@RequestParam(required = false) String keyword,
@RequestParam(required = false) String type,
@RequestParam(required = false) String status) {
startPage();
List<NoticeSendRecord> list = recordService.search(keyword, type, status);
return getDataTable(list);
}
}
@@ -0,0 +1,44 @@
package cn.universal.admin.system.notice;
import cn.universal.manager.notice.model.NoticeSendRequest;
import cn.universal.manager.notice.service.NoticeService;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/** 通知发送 */
@RestController
@RequestMapping("/admin/v1/notice")
public class NoticeSendController {
private final NoticeService noticeService;
@Autowired
public NoticeSendController(NoticeService noticeService) {
this.noticeService = noticeService;
}
@PostMapping("/send")
public Map<String, Object> send(@RequestBody Map<String, Object> request) {
Long templateId = Long.valueOf(request.get("templateId").toString());
String receivers = (String) request.get("receivers");
Object params = request.get("params");
if (templateId == null) {
throw new IllegalArgumentException("模板ID不能为空");
}
NoticeSendRequest req = new NoticeSendRequest();
req.setTemplateId(templateId);
req.setReceivers(receivers);
if (params instanceof Map) {
req.setParams((Map<String, Object>) params);
}
noticeService.send(req);
return Map.of("code", 0, "msg", "发送成功");
}
}
@@ -0,0 +1,165 @@
package cn.universal.admin.system.notice;
import cn.universal.admin.common.utils.SecurityUtils;
import cn.universal.admin.system.web.BaseController;
import cn.universal.manager.notice.dto.NoticeTemplateDTO;
import cn.universal.manager.notice.model.NoticeTemplate;
import cn.universal.manager.notice.service.NoticeTemplateService;
import cn.universal.persistence.page.TableDataInfo;
import cn.universal.persistence.query.AjaxResult;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/admin/v1/notice/template")
public class NoticeTemplateController extends BaseController {
private final NoticeTemplateService noticeTemplateService;
@Autowired
public NoticeTemplateController(NoticeTemplateService noticeTemplateService) {
this.noticeTemplateService = noticeTemplateService;
}
@GetMapping("/list")
public TableDataInfo<NoticeTemplateDTO> list(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String name,
@RequestParam(required = false) String channelType) {
startPage();
List<NoticeTemplate> list = noticeTemplateService.search(name, channelType, null);
List<NoticeTemplateDTO> dtoList =
list.stream()
.map(
template -> {
NoticeTemplateDTO dto = new NoticeTemplateDTO();
dto.setId(template.getId());
dto.setName(template.getName());
dto.setChannelType(template.getChannelType());
dto.setChannelId(template.getChannelId());
dto.setContent(template.getContent());
dto.setReceivers(template.getReceivers());
dto.setStatus(template.getStatus());
dto.setRemark(template.getRemark());
dto.setCreator(template.getCreator());
dto.setCreateTime(template.getCreateTime());
dto.setUpdateTime(template.getUpdateTime());
return dto;
})
.toList();
return getDataTable(dtoList);
}
@PostMapping("/save")
public AjaxResult save(@RequestBody NoticeTemplateDTO templateDTO) {
try {
NoticeTemplate template = new NoticeTemplate();
template.setId(templateDTO.getId());
template.setName(templateDTO.getName());
template.setChannelType(templateDTO.getChannelType());
template.setChannelId(templateDTO.getChannelId());
template.setContent(templateDTO.getContent());
template.setReceivers(templateDTO.getReceivers());
template.setStatus(templateDTO.getStatus());
template.setRemark(templateDTO.getRemark());
template.setCreator(SecurityUtils.getUnionId());
template.setCreateTime(templateDTO.getCreateTime());
template.setUpdateTime(templateDTO.getUpdateTime());
noticeTemplateService.save(template);
return AjaxResult.success("操作成功");
} catch (Exception e) {
return AjaxResult.error("操作失败: " + e.getMessage());
}
}
@PostMapping("/delete")
public AjaxResult delete(@RequestBody List<Long> ids) {
try {
if (ids != null && !ids.isEmpty()) {
noticeTemplateService.deleteBatch(ids);
return AjaxResult.success("删除成功");
} else {
return AjaxResult.success("未提供有效的ID");
}
} catch (Exception e) {
return AjaxResult.success("删除失败: " + e.getMessage());
}
}
/** 批量启用通知模板 */
@PostMapping("/enable")
public AjaxResult enable(@RequestBody List<Long> ids) {
try {
if (ids != null && !ids.isEmpty()) {
noticeTemplateService.enableBatch(ids);
return AjaxResult.success("启用成功");
} else {
return AjaxResult.success("未提供有效的ID");
}
} catch (Exception e) {
return AjaxResult.error("启用失败: " + e.getMessage());
}
}
/** 批量停用通知模板 */
@PostMapping("/disable")
public AjaxResult disable(@RequestBody List<Long> ids) {
try {
if (ids != null && !ids.isEmpty()) {
noticeTemplateService.disableBatch(ids);
return AjaxResult.success("停用成功");
} else {
return AjaxResult.success("未提供有效的ID");
}
} catch (Exception e) {
return AjaxResult.error("停用失败: " + e.getMessage());
}
}
@GetMapping("/get")
public NoticeTemplateDTO get(@RequestParam Long id) {
try {
NoticeTemplate template = noticeTemplateService.getById(id);
if (template == null) {
return null;
}
NoticeTemplateDTO dto = new NoticeTemplateDTO();
dto.setId(template.getId());
dto.setName(template.getName());
dto.setChannelType(template.getChannelType());
dto.setChannelId(template.getChannelId());
dto.setContent(template.getContent());
dto.setReceivers(template.getReceivers());
dto.setStatus(template.getStatus());
dto.setRemark(template.getRemark());
dto.setCreator(template.getCreator());
dto.setCreateTime(template.getCreateTime());
dto.setUpdateTime(template.getUpdateTime());
return dto;
} catch (Exception e) {
return null;
}
}
@PostMapping("/test")
public AjaxResult test(@RequestBody Map<String, Object> request) {
Long templateId = Long.valueOf(request.get("templateId").toString());
String receivers = (String) request.get("receivers");
Object params = request.get("params");
noticeTemplateService.testTemplate(templateId, receivers, params);
return AjaxResult.success("成功");
}
}
@@ -0,0 +1,145 @@
/*
*
* Copyright (c) 2025, IoT-Universal. All Rights Reserved.
*
* @Description: 本文件由 Aleo 开发并拥有版权,未经授权严禁擅自商用、复制或传播。
* @Author: Aleo
* @Email: wo8335224@gmail.com
* @Wechat: outlookFil
*
*
*/
package cn.universal.admin.system.web;
import cn.universal.admin.common.annotation.Log;
import cn.universal.admin.common.enums.BusinessType;
import cn.universal.admin.common.utils.SecurityUtils;
import cn.universal.persistence.entity.bo.RulegoChainBO;
import cn.universal.persistence.entity.vo.RulegoChainVO;
import cn.universal.persistence.page.TableDataInfo;
import cn.universal.persistence.query.AjaxResult;
import cn.universal.rule.rulego.service.RulegoChainService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* rulego规则链管理控制器
*
* @author Aleo
* @since 2025/01/15
*/
@RestController
@Tag(name = "rulego规则链管理", description = "rulego规则链管理")
@RequestMapping("/admin/v1/rulego")
public class RulegoChainController extends BaseController {
@Resource private RulegoChainService rulegoChainService;
@Operation(summary = "查询规则链列表")
@GetMapping("/chain/list")
public TableDataInfo<RulegoChainVO> pageList(RulegoChainBO bo) {
bo.setCreatorId(loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId());
startPage();
return getDataTable(rulegoChainService.queryRulegoChainList(bo));
}
@Operation(summary = "查询规则链详情")
@GetMapping("/chain/{id}")
public AjaxResult<RulegoChainVO> detail(@PathVariable @Parameter(description = "规则链ID") Long id) {
String creatorId = loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId();
RulegoChainVO rulegoChain = rulegoChainService.queryRulegoChainById(id, creatorId);
return AjaxResult.success(rulegoChain);
}
@Operation(summary = "根据rulegoId查询规则链详情")
@GetMapping("/chain/detail")
public AjaxResult<RulegoChainVO> detailByRulegoId(
@RequestParam @Parameter(description = "rulego规则链ID") String rulegoId) {
RulegoChainVO rulegoChain = rulegoChainService.queryRulegoChainByRulegoId(rulegoId);
return AjaxResult.success(rulegoChain);
}
@Operation(summary = "新增规则链")
@PostMapping("/chain")
@Log(title = "新增rulego规则链", businessType = BusinessType.INSERT)
public AjaxResult<Long> addRulegoChain(@RequestBody RulegoChainBO bo) {
bo.setCreatorId(loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId());
bo.setCreatorName(loginIoTUnionUser(SecurityUtils.getUnionId()).getUsername());
Long chainId = rulegoChainService.saveRulegoChain(bo);
return AjaxResult.success(chainId);
}
@Operation(summary = "更新规则链")
@PutMapping("/chain")
@Log(title = "更新rulego规则链", businessType = BusinessType.UPDATE)
public AjaxResult<Void> updateRulegoChain(@RequestBody RulegoChainBO bo) {
bo.setCreatorId(loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId());
boolean result = rulegoChainService.updateRulegoChain(bo);
return result ? AjaxResult.success() : AjaxResult.error("更新失败");
}
@Operation(summary = "删除规则链")
@DeleteMapping("/chain/{id}")
@Log(title = "删除rulego规则链", businessType = BusinessType.DELETE)
public AjaxResult<Void> deleteRulegoChain(
@PathVariable @Parameter(description = "规则链ID") Long id) {
String creatorId = loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId();
boolean result = rulegoChainService.deleteRulegoChain(id, creatorId);
return result ? AjaxResult.success() : AjaxResult.error("删除失败");
}
@Operation(summary = "部署规则链")
@PostMapping("/chain/{id}/deploy")
@Log(title = "部署rulego规则链", businessType = BusinessType.UPDATE)
public AjaxResult<Void> deployRulegoChain(
@PathVariable @Parameter(description = "规则链ID") Long id) {
String creatorId = loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId();
boolean result = rulegoChainService.deployRulegoChain(id, creatorId);
return result ? AjaxResult.success() : AjaxResult.error("部署失败");
}
@Operation(summary = "停止规则链")
@PostMapping("/chain/{id}/stop")
@Log(title = "停止rulego规则链", businessType = BusinessType.UPDATE)
public AjaxResult<Void> stopRulegoChain(@PathVariable @Parameter(description = "规则链ID") Long id) {
String creatorId = loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId();
boolean result = rulegoChainService.stopRulegoChain(id, creatorId);
return result ? AjaxResult.success() : AjaxResult.error("停止失败");
}
@Operation(summary = "同步规则链DSL")
@PostMapping("/chain/{id}/sync")
@Log(title = "同步rulego规则链DSL", businessType = BusinessType.UPDATE)
public AjaxResult<Void> syncRulegoChainDsl(
@PathVariable @Parameter(description = "规则链ID") Long id) {
String creatorId = loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId();
RulegoChainVO rulegoChain = rulegoChainService.queryRulegoChainById(id, creatorId);
boolean result = rulegoChainService.syncRulegoChainDsl(rulegoChain.getRulegoId());
return result ? AjaxResult.success() : AjaxResult.error("同步失败");
}
@Operation(summary = "获取设计器URL")
@GetMapping("/chain/{id}/designer")
public AjaxResult<String> getDesignerUrl(@PathVariable @Parameter(description = "规则链ID") Long id) {
String creatorId = loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId();
RulegoChainVO rulegoChain = rulegoChainService.queryRulegoChainById(id, creatorId);
String designerUrl = rulegoChainService.getDesignerUrl(rulegoChain.getRulegoId());
AjaxResult<String> result = new AjaxResult<>();
result.setCode(0);
result.setMsg("success");
result.setData(designerUrl);
return result;
}
}
@@ -0,0 +1,60 @@
/*
*
* Copyright (c) 2025, IoT-Universal. All Rights Reserved.
*
* @Description: 本文件由 Aleo 开发并拥有版权,未经授权严禁擅自商用、复制或传播。
* @Author: Aleo
* @Email: wo8335224@gmail.com
* @Wechat: outlookFil
*
*
*/
package cn.universal.common.constant;
/**
* 摄像头操作命令枚举
* 直接对应物模型函数名
*
* @version 1.0
* @since 2025/1/15
*/
public enum CameraCommand {
// 摄像头相关物模型函数
CAMERA_LIVE_STREAM("cameraLiveStream"),
CAMERA_PLAYBACK("cameraPlayback"),
CAMERA_SNAPSHOT("cameraSnapshot"),
CAMERA_TURN("cameraTurn"),
CAMERA_FLIP_SET("cameraFlipSet");
private final String functionName;
CameraCommand(String functionName) {
this.functionName = functionName;
}
/**
* 获取物模型函数名
*
* @return 物模型函数名
*/
public String getFunctionName() {
return functionName;
}
/**
* 根据物模型函数名获取对应的命令枚举
*
* @param functionName 物模型函数名
* @return 对应的命令枚举,如果未找到返回null
*/
public static CameraCommand fromFunctionName(String functionName) {
for (CameraCommand command : values()) {
if (command.functionName.equals(functionName)) {
return command;
}
}
return null;
}
}
@@ -0,0 +1,20 @@
package cn.universal.common.event.processer;
import cn.universal.common.event.EventMessage;
/**
* UDP下行指令处理器接口
*
* @author Aleo
* @version 1.0
* @since 2025/1/9
*/
public interface UdpDownProcessor {
/**
* 处理UDP下行指令事件
*
* @param message 事件消息字符串
*/
void handleUdpDownEvent(EventMessage message);
}
@@ -0,0 +1,68 @@
/*
*
* Copyright (c) 2025, IoT-Universal. All Rights Reserved.
*
* @Description: 本文件由 Aleo 开发并拥有版权,未经授权严禁擅自商用、复制或传播。
* @Author: Aleo
* @Email: wo8335224@gmail.com
* @Wechat: outlookFil
*
*
*/
package cn.universal.common.monitor;
import cn.hutool.core.util.StrUtil;
import cn.universal.common.utils.DingTalkUtil;
import java.util.HashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
/** 网络监测单例 */
@Slf4j
public class NetworkMonitorSingleton {
private NetworkMonitorSingleton() {}
private static class SingleTonHolder {
private static NetworkMonitorSingleton INSTANCE = new NetworkMonitorSingleton();
}
public static NetworkMonitorSingleton getInstance() {
return SingleTonHolder.INSTANCE;
}
private static volatile Map<String, Boolean> networkMap = new HashMap<>();
public static void freshConnectionStatus(String serverURI, Boolean status) {
if (StrUtil.isBlank(serverURI)) {
return;
}
boolean lastStatusNormal =
!networkMap.containsKey(serverURI) || networkMap.get(serverURI).equals(Boolean.TRUE);
if (lastStatusNormal && Boolean.FALSE.equals(status)) {
DingTalkUtil.send("IoT Universal,[" + serverURI + "] 断开异常!");
log.warn("[网络监控] 服务断开: serverURI={}, status={},请注意!", serverURI, status);
}
boolean lastStatusAbnormal =
!networkMap.containsKey(serverURI) || networkMap.get(serverURI).equals(Boolean.FALSE);
if (lastStatusAbnormal && Boolean.TRUE.equals(status)) {
DingTalkUtil.send("IoT Universal,指标[" + serverURI + "] 状态正常!!!");
}
if (!networkMap.containsKey(serverURI)) {
networkMap.put(serverURI, status);
}
if (!networkMap.get(serverURI).equals(status)) {
networkMap.put(serverURI, status);
}
}
public static void removeMosquittoServer(String serverId) {
NetworkMonitorSingleton.networkMap.remove(serverId);
}
public static Map<String, Boolean> getConnectionStatus() {
return networkMap;
}
}
@@ -0,0 +1,111 @@
/*
*
* Copyright (c) 2025, IoT-Universal. All Rights Reserved.
*
* @Description: 本文件由 Aleo 开发并拥有版权,未经授权严禁擅自商用、复制或传播。
* @Author: Aleo
* @Email: wo8335224@gmail.com
* @Wechat: outlookFil
*
*
*/
package cn.universal.common.utils;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DingTalkUtil {
// @Value("${notice.dingTalk.dingTalk-addr}")
private static final String token =
"https://oapi.dingtalk.com/robot/send?access_token=f731085dd9ef094114794a0f5ff2cc09e017d18acea205415cc03039af472661";
// @Value("${notice.dingTalk.dingTalk-secret}")
private static final String secret =
"SEC34946b12a19f67f32f0e7a1a42b1ca110f6c2ddb224214ce474e594b025efd1e";
// @Value("${notice.dingTalk.sign}")
private static final String mark = "platform Universal";
private static Set<String> prodProfileActive =
Stream.of("test","test2", "prod").collect(Collectors.toSet());
// 第三方调用失败钉钉通知
public static void send(String msg) {
String actProfiles = SpringUtil.getActiveProfile();
if (StrUtil.isBlank(actProfiles) || !prodProfileActive.contains(actProfiles)) {
log.warn("非正式环境,中止发送钉钉告警,msg={}", msg);
return;
}
try {
String res = "";
Long timestamp = System.currentTimeMillis();
String stringToSign = timestamp + "\n" + secret;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
String sign = URLEncoder.encode(Base64.encode(signData), "UTF-8");
String url = token + "&timestamp=" + timestamp + "&sign=" + sign;
JSONObject jsonObject = new JSONObject();
JSONObject text = new JSONObject();
JSONObject at = new JSONObject();
msg = mark + ":" + actProfiles + ":" + msg;
jsonObject.set("msgtype", "text");
text.set("content", msg);
jsonObject.set("text", text);
List<String> phone = new ArrayList<>();
at.set("atMobiles", phone);
jsonObject.set("at", at);
String response = HttpUtil.post(url, JSONUtil.toJsonStr(jsonObject));
JSONObject result = JSONUtil.parseObj(response);
Integer errCode = result.getInt("errcode");
if (errCode == 0) {
res = "成功";
} else {
switch (errCode) {
case -1:
res = "系统繁忙";
break;
case 88:
res = "鉴权异常";
break;
case 404:
res = "请求的URI地址不存在";
break;
case 34015:
res = "发送群会话消息失败";
break;
case 34016:
res = "会话消息的内容超长";
break;
case 40001:
res = "获取access_token时Secret错误,或者access_token无效";
break;
case 40002:
res = "不合法的凭证类型";
break;
default:
res = result.getStr("errmsg");
break;
}
}
if (!"".equals(res) && !"成功".equals(res)) {
log.error("钉钉系统监测推送失败,原因{}", res);
}
} catch (Exception e) {
log.error("钉钉调用异常:", e);
}
}
}
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>cn-universal-framework</artifactId>
<groupId>cn.universal.iot</groupId>
<version>1.5-SNAPSHOT</version>
</parent>
<artifactId>cn-universal-notice</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--阿里短信-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>2.0.24</version>
<exclusions>
<exclusion>
<artifactId>jaxb-impl</artifactId>
<groupId>com.sun.xml.bind</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
<!--排除这个slf4j-log4j12-->
<groupId>org.springframework.boot</groupId>
<version>${spring-boot.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<artifactId>cn-universal-core</artifactId>
<groupId>cn.universal.iot</groupId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>cn.universal.iot</groupId>
<artifactId>cn-universal-persistence</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.57</version>
</dependency>
<!-- 腾讯云短信 -->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.1.822</version>
</dependency>
<!-- JavaMail -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>${spring-boot.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,97 @@
package cn.universal.manager.notice.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.Date;
public class NoticeChannelDTO implements Serializable {
private Long id;
private String name;
@JsonProperty("channelType")
private String channelType;
private Object config;
private String status;
private String remark;
private String creator;
@JsonProperty("createTime")
private Date createTime;
@JsonProperty("updateTime")
private Date updateTime;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getChannelType() {
return channelType;
}
public void setChannelType(String channelType) {
this.channelType = channelType;
}
public Object getConfig() {
return config;
}
public void setConfig(Object config) {
this.config = config;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getCreator() {
return creator;
}
public void setCreator(String creator) {
this.creator = creator;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
@@ -0,0 +1,85 @@
package cn.universal.manager.notice.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.Date;
public class NoticeConfigDTO implements Serializable {
private Long id;
private String name;
private String type;
private Object config;
private String remark;
private String creator;
@JsonProperty("createTime")
private Date createTime;
@JsonProperty("updateTime")
private Date updateTime;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Object getConfig() {
return config;
}
public void setConfig(Object config) {
this.config = config;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getCreator() {
return creator;
}
public void setCreator(String creator) {
this.creator = creator;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
@@ -0,0 +1,117 @@
package cn.universal.manager.notice.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.Date;
public class NoticeTemplateDTO implements Serializable {
private Long id;
private String name;
@JsonProperty("channelType")
private String channelType;
@JsonProperty("channelId")
private Long channelId;
private String content;
private String receivers;
private String status;
private String remark;
private String creator;
@JsonProperty("createTime")
private Date createTime;
@JsonProperty("updateTime")
private Date updateTime;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getChannelType() {
return channelType;
}
public void setChannelType(String channelType) {
this.channelType = channelType;
}
public Long getChannelId() {
return channelId;
}
public void setChannelId(Long channelId) {
this.channelId = channelId;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getReceivers() {
return receivers;
}
public void setReceivers(String receivers) {
this.receivers = receivers;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getCreator() {
return creator;
}
public void setCreator(String creator) {
this.creator = creator;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
@@ -0,0 +1,6 @@
package cn.universal.manager.notice.mapper;
import cn.universal.manager.notice.model.NoticeChannel;
import tk.mybatis.mapper.common.Mapper;
public interface NoticeChannelMapper extends Mapper<NoticeChannel> {}
@@ -0,0 +1,6 @@
package cn.universal.manager.notice.mapper;
import cn.universal.manager.notice.model.NoticeSendRecord;
import cn.universal.persistence.common.BaseMapper;
public interface NoticeSendRecordMapper extends BaseMapper<NoticeSendRecord> {}
@@ -0,0 +1,6 @@
package cn.universal.manager.notice.mapper;
import cn.universal.manager.notice.model.NoticeTemplate;
import tk.mybatis.mapper.common.Mapper;
public interface NoticeTemplateMapper extends Mapper<NoticeTemplate> {}
@@ -0,0 +1,102 @@
package cn.universal.manager.notice.model;
import jakarta.persistence.Column;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.io.Serializable;
import java.util.Date;
@Table(name = "notice_channel")
public class NoticeChannel implements Serializable {
@Id private Long id;
private String name;
@Column(name = "channel_type")
private String channelType;
@Column(columnDefinition = "TEXT")
private String config;
private String status;
private String remark;
private String creator;
@Column(name = "create_time")
private Date createTime;
@Column(name = "update_time")
private Date updateTime;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getChannelType() {
return channelType;
}
public void setChannelType(String channelType) {
this.channelType = channelType;
}
public String getConfig() {
return config;
}
public void setConfig(String config) {
this.config = config;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getCreator() {
return creator;
}
public void setCreator(String creator) {
this.creator = creator;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
@@ -0,0 +1,83 @@
package cn.universal.manager.notice.model;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.io.Serializable;
import java.util.Date;
@Table(name = "notice_send_record")
public class NoticeSendRecord implements Serializable {
@Id private Long id;
private Long templateId;
private Long configId;
private String receivers;
private String params;
private String status;
private String result;
private Date sendTime;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getTemplateId() {
return templateId;
}
public void setTemplateId(Long templateId) {
this.templateId = templateId;
}
public Long getConfigId() {
return configId;
}
public void setConfigId(Long configId) {
this.configId = configId;
}
public String getReceivers() {
return receivers;
}
public void setReceivers(String receivers) {
this.receivers = receivers;
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public Date getSendTime() {
return sendTime;
}
public void setSendTime(Date sendTime) {
this.sendTime = sendTime;
}
}
@@ -0,0 +1,34 @@
package cn.universal.manager.notice.model;
import java.util.Map;
public class NoticeSendRequest {
private Long templateId;
private Map<String, Object> params;
private String receivers;
public Long getTemplateId() {
return templateId;
}
public void setTemplateId(Long templateId) {
this.templateId = templateId;
}
public Map<String, Object> getParams() {
return params;
}
public void setParams(Map<String, Object> params) {
this.params = params;
}
public String getReceivers() {
return receivers;
}
public void setReceivers(String receivers) {
this.receivers = receivers;
}
}
@@ -0,0 +1,120 @@
package cn.universal.manager.notice.model;
import jakarta.persistence.Column;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.io.Serializable;
import java.util.Date;
@Table(name = "notice_template")
public class NoticeTemplate implements Serializable {
@Id private Long id;
private String name;
@Column(name = "channel_type")
private String channelType;
@Column(name = "channel_id")
private Long channelId;
private String content;
private String receivers;
private String status;
private String remark;
private String creator;
@Column(name = "create_time")
private Date createTime;
@Column(name = "update_time")
private Date updateTime;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getChannelType() {
return channelType;
}
public void setChannelType(String channelType) {
this.channelType = channelType;
}
public Long getChannelId() {
return channelId;
}
public void setChannelId(Long channelId) {
this.channelId = channelId;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getReceivers() {
return receivers;
}
public void setReceivers(String receivers) {
this.receivers = receivers;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getCreator() {
return creator;
}
public void setCreator(String creator) {
this.creator = creator;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
@@ -0,0 +1,19 @@
package cn.universal.manager.notice.service;
import cn.universal.manager.notice.model.NoticeChannel;
import java.util.List;
public interface NoticeChannelService {
List<NoticeChannel> list();
List<NoticeChannel> search(String name, String channelType, String status);
void save(NoticeChannel channel);
void delete(Long id);
void deleteBatch(List<Long> ids);
NoticeChannel getById(Long id);
}
@@ -0,0 +1,13 @@
package cn.universal.manager.notice.service;
import cn.universal.manager.notice.model.NoticeSendRecord;
import java.util.List;
public interface NoticeRecordService {
void save(NoticeSendRecord record);
List<NoticeSendRecord> list();
List<NoticeSendRecord> search(String keyword, String type, String status);
}
@@ -0,0 +1,13 @@
package cn.universal.manager.notice.service;
import cn.universal.manager.notice.model.NoticeSendRequest;
import cn.universal.manager.notice.service.channel.NoticeSendResult;
public interface NoticeService {
void send(NoticeSendRequest req);
default NoticeSendResult sendR(NoticeSendRequest request) {
return null;
}
}
@@ -0,0 +1,25 @@
package cn.universal.manager.notice.service;
import cn.universal.manager.notice.model.NoticeTemplate;
import java.util.List;
public interface NoticeTemplateService {
List<NoticeTemplate> list();
void save(NoticeTemplate template);
void delete(Long id);
NoticeTemplate getById(Long id);
List<NoticeTemplate> search(String name, String channelType, String status);
void deleteBatch(List<Long> ids);
void testTemplate(Long templateId, String receivers, Object params);
void enableBatch(List<Long> ids);
void disableBatch(List<Long> ids);
}
@@ -0,0 +1,60 @@
package cn.universal.manager.notice.service.channel;
import cn.hutool.json.JSONUtil;
import cn.hutool.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** 抽象通知渠道基类 提供通用的配置解析和日志记录功能 */
public abstract class AbstractNoticeSendChannel implements NoticeSendChannel {
protected final Logger logger = LoggerFactory.getLogger(getClass());
/** 从配置中解析JSON对象 */
protected JSONObject parseConfig(cn.universal.manager.notice.model.NoticeChannel config) {
if (config == null || config.getConfig() == null) {
return new JSONObject();
}
try {
return JSONUtil.parseObj(config.getConfig());
} catch (Exception e) {
logger.error("解析配置失败: {}", config.getConfig(), e);
return new JSONObject();
}
}
/** 记录发送日志 */
protected void logSend(
String channelType, String content, String receivers, boolean success, String message) {
if (success) {
logger.info("[{}] 发送成功 - 接收者: {}, 内容: {}", channelType, receivers, content);
} else {
logger.error("[{}] 发送失败 - 接收者: {}, 内容: {}, 错误: {}", channelType, receivers, content, message);
}
}
/** 验证配置是否完整 */
protected boolean validateConfig(JSONObject config, String... requiredFields) {
for (String field : requiredFields) {
if (!config.containsKey(field)
|| config.getStr(field) == null
|| config.getStr(field).trim().isEmpty()) {
logger.error("配置缺少必需字段: {}", field);
return false;
}
}
return true;
}
/** 构建并记录发送结果 */
protected NoticeSendResult buildAndLogResult(
String channelType, String content, String receivers, boolean success, String errorMessage) {
logSend(channelType, content, receivers, success, errorMessage);
return NoticeSendResult.builder()
.success(success)
.receivers(receivers)
.content(content)
.errorMessage(errorMessage)
.build();
}
}
@@ -0,0 +1,55 @@
package cn.universal.manager.notice.service.channel;
import cn.hutool.json.JSONObject;
import java.util.Map;
import org.springframework.stereotype.Component;
@Component
public class AudioAliNoticeSendChannel extends AbstractNoticeSendChannel {
@Override
public boolean support(String type) {
return "audio_ali".equalsIgnoreCase(type);
}
@Override
public NoticeSendResult send(
String content,
String receivers,
cn.universal.manager.notice.model.NoticeChannel config,
Map<String, Object> params) {
try {
JSONObject configObj = parseConfig(config);
if (!validateConfig(configObj, "accessKey", "secretKey", "signName", "templateCode")) {
String errMsg = "配置不完整";
logSend("AudioAli", content, receivers, false, errMsg);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
// TODO: 实现阿里云语音API调用
logger.info(
"阿里云语音发送 - 接收者: {}, 内容: {}, 配置: {}", receivers, content, configObj.toString());
logSend("AudioAli", content, receivers, true, "发送成功(模拟)");
return NoticeSendResult.builder()
.success(true)
.receivers(receivers)
.content(content)
.errorMessage(null)
.build();
} catch (Exception e) {
String errMsg = "发送异常: " + e.getMessage();
logSend("AudioAli", content, receivers, false, errMsg);
logger.error("阿里云语音发送异常", e);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
}
}
@@ -0,0 +1,97 @@
package cn.universal.manager.notice.service.channel;
import cn.hutool.json.JSONObject;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.tts.v20190823.TtsClient;
import com.tencentcloudapi.tts.v20190823.models.TextToVoiceRequest;
import com.tencentcloudapi.tts.v20190823.models.TextToVoiceResponse;
import java.util.Map;
import org.springframework.stereotype.Component;
@Component
public class AudioTencentNoticeSendChannel extends AbstractNoticeSendChannel {
@Override
public boolean support(String type) {
return "audio_tencent".equalsIgnoreCase(type);
}
@Override
public NoticeSendResult send(
String content,
String receivers,
cn.universal.manager.notice.model.NoticeChannel config,
Map<String, Object> params) {
try {
JSONObject configObj = parseConfig(config);
if (!validateConfig(
configObj, "secretId", "secretKey", "signName", "templateCode", "receivers")) {
String errMsg = "配置不完整";
logSend("AudioTencent", content, receivers, false, errMsg);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
Credential cred =
new Credential(configObj.getStr("secretId"), configObj.getStr("secretKey"));
TtsClient client = new TtsClient(cred, "ap-guangzhou");
String[] templateParams;
if (params != null && !params.isEmpty()) {
templateParams = params.values().stream().map(Object::toString).toArray(String[]::new);
} else {
templateParams = new String[] {content};
}
TextToVoiceRequest request = new TextToVoiceRequest();
request.setText(content);
request.setSessionId(String.valueOf(System.currentTimeMillis()));
request.setModelType(1L); // 默认模型
request.setVolume(0.0f); // 默认音量
request.setSpeed(0.0f); // 默认语速
request.setProjectId(0L); // 默认项目ID
request.setSampleRate(16000L); // 采样率
TextToVoiceResponse response = client.TextToVoice(request);
if (response.getAudio() != null) {
logSend("AudioTencent", content, receivers, true, "发送成功");
return NoticeSendResult.builder()
.success(true)
.receivers(receivers)
.content(content)
.errorMessage(null)
.build();
} else {
String errMsg = "腾讯云返回空响应";
logSend("AudioTencent", content, receivers, false, errMsg);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
} catch (TencentCloudSDKException e) {
String errMsg = "腾讯云SDK异常: " + e.getMessage();
logSend("AudioTencent", content, receivers, false, errMsg);
logger.error("腾讯云语音发送异常", e);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
} catch (Exception e) {
String errMsg = "发送异常: " + e.getMessage();
logSend("AudioTencent", content, receivers, false, errMsg);
logger.error("腾讯云语音发送异常", e);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
}
}
@@ -0,0 +1,90 @@
package cn.universal.manager.notice.service.channel;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.json.JSONObject;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
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;
@Component
public class DingTalkNoticeSendChannel extends AbstractNoticeSendChannel {
private final RestTemplate restTemplate = new RestTemplate();
@Override
public boolean support(String type) {
return "dingTalk".equalsIgnoreCase(type);
}
@Override
public NoticeSendResult send(
String content,
String receivers,
cn.universal.manager.notice.model.NoticeChannel config,
Map<String, Object> params) {
try {
JSONObject configObj = parseConfig(config);
if (!validateConfig(configObj, "webhook")) {
String errMsg = "配置不完整";
return buildAndLogResult("DingTalk", content, receivers, false, errMsg);
}
String webhook = configObj.getStr("webhook");
String secret = configObj.getStr("secret");
JSONObject message = new JSONObject();
message.set("msgtype", "text");
JSONObject text = new JSONObject();
text.set("content", content);
message.set("text", text);
if (StrUtil.isNotBlank(receivers)) {
text.set("atMobiles", StrUtil.split(receivers, ',', true, true));
message.set("at", text);
}
if (secret != null && !secret.trim().isEmpty()) {
long timestamp = System.currentTimeMillis();
String stringToSign = timestamp + "\n" + secret;
String sign = calculateHmacSHA256(stringToSign, secret);
String signedUrl = webhook + "&timestamp=" + timestamp + "&sign=" + sign;
webhook = signedUrl;
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<>(message.toString(), headers);
ResponseEntity<String> response = restTemplate.postForEntity(webhook, request, String.class);
if (response.getStatusCode().is2xxSuccessful()) {
JSONObject responseBody = JSONUtil.parseObj(response.getBody());
if (responseBody.getInt("errcode") == 0) {
return buildAndLogResult("DingTalk", content, receivers, true, null);
} else {
String errMsg = "钉钉返回错误: " + responseBody.getStr("errmsg");
return buildAndLogResult("DingTalk", content, receivers, false, errMsg);
}
} else {
String errMsg = "HTTP请求失败: " + response.getStatusCode();
return buildAndLogResult("DingTalk", content, receivers, false, errMsg);
}
} catch (Exception e) {
String errMsg = "发送异常: " + e.getMessage();
logger.error("钉钉发送异常", e);
return buildAndLogResult("DingTalk", content, receivers, false, errMsg);
}
}
/** 计算HMAC-SHA256签名 */
private String calculateHmacSHA256(String data, String key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec =
new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
mac.init(secretKeySpec);
byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
}
}
@@ -0,0 +1,63 @@
package cn.universal.manager.notice.service.channel;
import cn.hutool.json.JSONObject;
import java.util.Map;
import java.util.Properties;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.stereotype.Component;
@Component
public class EmailNoticeSendChannel extends AbstractNoticeSendChannel {
@Override
public boolean support(String type) {
return "email".equalsIgnoreCase(type);
}
@Override
public NoticeSendResult send(
String content,
String receivers,
cn.universal.manager.notice.model.NoticeChannel config,
Map<String, Object> params) {
try {
JSONObject configObj = parseConfig(config);
if (!validateConfig(
configObj, "smtpHost", "smtpPort", "username", "password", "fromEmail", "receivers")) {
String errMsg = "配置不完整";
return buildAndLogResult("Email", content, receivers, false, errMsg);
}
JavaMailSender mailSender = createMailSender(configObj);
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(configObj.getStr("fromEmail"));
message.setTo(receivers.split(","));
message.setSubject("系统通知");
message.setText(content);
mailSender.send(message);
return buildAndLogResult("Email", content, receivers, true, null);
} catch (Exception e) {
String errMsg = "发送异常: " + e.getMessage();
logger.error("邮件发送异常", e);
return buildAndLogResult("Email", content, receivers, false, errMsg);
}
}
/** 创建邮件发送器 */
private JavaMailSender createMailSender(JSONObject config) {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(config.getStr("smtpHost"));
mailSender.setPort(config.getInt("smtpPort"));
mailSender.setUsername(config.getStr("username"));
mailSender.setPassword(config.getStr("password"));
Properties props = mailSender.getJavaMailProperties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.debug", "false");
return mailSender;
}
}
@@ -0,0 +1,93 @@
package cn.universal.manager.notice.service.channel;
import cn.hutool.json.JSONUtil;
import cn.hutool.json.JSONObject;
import java.util.Map;
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;
@Component
public class FeishuNoticeSendChannel extends AbstractNoticeSendChannel {
private final RestTemplate restTemplate = new RestTemplate();
@Override
public boolean support(String type) {
return "feishu".equalsIgnoreCase(type);
}
@Override
public NoticeSendResult send(
String content,
String receivers,
cn.universal.manager.notice.model.NoticeChannel config,
Map<String, Object> params) {
try {
JSONObject configObj = parseConfig(config);
if (!validateConfig(configObj, "webhook")) {
String errMsg = "配置不完整";
logSend("Feishu", content, receivers, false, errMsg);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
String webhook = configObj.getStr("webhook");
JSONObject message = new JSONObject();
message.set("msg_type", "text");
JSONObject text = new JSONObject();
text.set("text", content);
message.set("content", text);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<>(message.toString(), headers);
ResponseEntity<String> response = restTemplate.postForEntity(webhook, request, String.class);
if (response.getStatusCode().is2xxSuccessful()) {
JSONObject responseBody = JSONUtil.parseObj(response.getBody());
if (responseBody.getInt("code") == 0) {
logSend("Feishu", content, receivers, true, "发送成功");
return NoticeSendResult.builder()
.success(true)
.receivers(receivers)
.content(content)
.errorMessage(null)
.build();
} else {
String errMsg = "飞书返回错误: " + responseBody.getStr("msg");
logSend("Feishu", content, receivers, false, errMsg);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
} else {
String errMsg = "HTTP请求失败: " + response.getStatusCode();
logSend("Feishu", content, receivers, false, errMsg);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
} catch (Exception e) {
String errMsg = "发送异常: " + e.getMessage();
logSend("Feishu", content, receivers, false, errMsg);
logger.error("飞书发送异常", e);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
}
}
@@ -0,0 +1,55 @@
package cn.universal.manager.notice.service.channel;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/** 通知渠道工厂类 用于管理和获取各种通知渠道 */
@Component
public class NoticeChannelFactory {
private final List<NoticeSendChannel> channels;
@Autowired
public NoticeChannelFactory(List<NoticeSendChannel> channels) {
this.channels = channels;
}
/**
* 根据渠道类型获取对应的通知渠道
*
* @param type 渠道类型
* @return 通知渠道实例
*/
public Optional<NoticeSendChannel> getChannel(String type) {
return channels.stream().filter(channel -> channel.support(type)).findFirst();
}
/**
* 检查是否支持指定的渠道类型
*
* @param type 渠道类型
* @return 是否支持
*/
public boolean supports(String type) {
return channels.stream().anyMatch(channel -> channel.support(type));
}
/**
* 获取所有支持的渠道类型
*
* @return 渠道类型列表
*/
public List<String> getSupportedTypes() {
return List.of(
"dingTalk", // 钉钉
"email", // 邮箱
"feishu", // 飞书
"sms_ali", // 阿里云短信
"sms_tencent", // 腾讯云短信
"audio_ali", // 阿里云语音
"audio_tencent" // 腾讯云语音
);
}
}
@@ -0,0 +1,12 @@
package cn.universal.manager.notice.service.channel;
import cn.universal.manager.notice.model.NoticeChannel;
import java.util.Map;
public interface NoticeSendChannel {
boolean support(String type);
NoticeSendResult send(
String content, String receivers, NoticeChannel config, Map<String, Object> params);
}
@@ -0,0 +1,86 @@
package cn.universal.manager.notice.service.channel;
public class NoticeSendResult {
private boolean success;
private String receivers;
private String content;
private String errorMessage;
public NoticeSendResult() {}
public NoticeSendResult(boolean success, String receivers, String content, String errorMessage) {
this.success = success;
this.receivers = receivers;
this.content = content;
this.errorMessage = errorMessage;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getReceivers() {
return receivers;
}
public void setReceivers(String receivers) {
this.receivers = receivers;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private boolean success;
private String receivers;
private String content;
private String errorMessage;
public Builder success(boolean success) {
this.success = success;
return this;
}
public Builder receivers(String receivers) {
this.receivers = receivers;
return this;
}
public Builder content(String content) {
this.content = content;
return this;
}
public Builder errorMessage(String errorMessage) {
this.errorMessage = errorMessage;
return this;
}
public NoticeSendResult build() {
return new NoticeSendResult(success, receivers, content, errorMessage);
}
}
}
@@ -0,0 +1,87 @@
package cn.universal.manager.notice.service.channel;
import cn.hutool.json.JSONObject;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import java.util.Map;
import org.springframework.stereotype.Component;
@Component
public class SmsAliNoticeSendChannel extends AbstractNoticeSendChannel {
@Override
public boolean support(String type) {
return "sms_ali".equalsIgnoreCase(type);
}
@Override
public NoticeSendResult send(
String content,
String receivers,
cn.universal.manager.notice.model.NoticeChannel config,
Map<String, Object> params) {
try {
JSONObject configObj = parseConfig(config);
if (!validateConfig(
configObj, "accessKey", "secretKey", "signName", "templateCode", "receivers")) {
String errMsg = "配置不完整";
logSend("SmsAli", content, receivers, false, errMsg);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
Config aliConfig =
new Config()
.setAccessKeyId(configObj.getStr("accessKey"))
.setAccessKeySecret(configObj.getStr("secretKey"))
.setEndpoint("dysmsapi.aliyuncs.com");
Client client = new Client(aliConfig);
JSONObject templateParam = new JSONObject();
if (params != null) {
params.forEach(templateParam::set);
} else {
templateParam.set("message", content);
}
SendSmsRequest request =
new SendSmsRequest()
.setPhoneNumbers(receivers)
.setSignName(configObj.getStr("signName"))
.setTemplateCode(configObj.getStr("templateCode"))
.setTemplateParam(templateParam.toString());
SendSmsResponse response = client.sendSms(request);
if ("OK".equals(response.getBody().getCode())) {
logSend("SmsAli", content, receivers, true, "发送成功");
return NoticeSendResult.builder()
.success(true)
.receivers(receivers)
.content(content)
.errorMessage(null)
.build();
} else {
String errMsg = "阿里云返回错误: " + response.getBody().getMessage();
logSend("SmsAli", content, receivers, false, errMsg);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
} catch (Exception e) {
String errMsg = "发送异常: " + e.getMessage();
logSend("SmsAli", content, receivers, false, errMsg);
logger.error("阿里云短信发送异常", e);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
}
}
@@ -0,0 +1,107 @@
package cn.universal.manager.notice.service.channel;
import cn.hutool.json.JSONObject;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
import java.util.Map;
import org.springframework.stereotype.Component;
@Component
public class SmsTencentNoticeSendChannel extends AbstractNoticeSendChannel {
@Override
public boolean support(String type) {
return "sms_tencent".equalsIgnoreCase(type);
}
@Override
public NoticeSendResult send(
String content,
String receivers,
cn.universal.manager.notice.model.NoticeChannel config,
Map<String, Object> params) {
try {
JSONObject configObj = parseConfig(config);
if (!validateConfig(
configObj, "secretId", "secretKey", "signName", "templateCode", "receivers")) {
String errMsg = "配置不完整";
logSend("SmsTencent", content, receivers, false, errMsg);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
Credential cred =
new Credential(configObj.getStr("secretId"), configObj.getStr("secretKey"));
SmsClient client = new SmsClient(cred, "ap-guangzhou");
String[] templateParams;
if (params != null && !params.isEmpty()) {
templateParams = params.values().stream().map(Object::toString).toArray(String[]::new);
} else {
templateParams = new String[] {content};
}
SendSmsRequest request = new SendSmsRequest();
request.setSmsSdkAppId("1400000000"); // 需要配置实际的SDK AppId
request.setSignName(configObj.getStr("signName"));
request.setTemplateId(configObj.getStr("templateCode"));
request.setTemplateParamSet(templateParams);
request.setPhoneNumberSet(new String[] {receivers});
SendSmsResponse response = client.SendSms(request);
if (response.getSendStatusSet() != null && response.getSendStatusSet().length > 0) {
String status = response.getSendStatusSet()[0].getCode();
if ("Ok".equals(status)) {
logSend("SmsTencent", content, receivers, true, "发送成功");
return NoticeSendResult.builder()
.success(true)
.receivers(receivers)
.content(content)
.errorMessage(null)
.build();
} else {
String errMsg = "腾讯云返回错误: " + response.getSendStatusSet()[0].getMessage();
logSend("SmsTencent", content, receivers, false, errMsg);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
} else {
String errMsg = "腾讯云返回空响应";
logSend("SmsTencent", content, receivers, false, errMsg);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
} catch (TencentCloudSDKException e) {
String errMsg = "腾讯云SDK异常: " + e.getMessage();
logSend("SmsTencent", content, receivers, false, errMsg);
logger.error("腾讯云短信发送异常", e);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
} catch (Exception e) {
String errMsg = "发送异常: " + e.getMessage();
logSend("SmsTencent", content, receivers, false, errMsg);
logger.error("腾讯云短信发送异常", e);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
}
}
@@ -0,0 +1,28 @@
package cn.universal.manager.notice.service.channel;
import cn.universal.manager.notice.model.NoticeChannel;
import java.util.Map;
import org.springframework.stereotype.Component;
@Component
public class WeComNoticeSendChannel implements NoticeSendChannel {
@Override
public boolean support(String type) {
return "wecom".equalsIgnoreCase(type);
}
@Override
public NoticeSendResult send(
String content, String receivers, NoticeChannel config, Map<String, Object> params) {
// 解析config.getConfig()为webhook等
// TODO: 发送企业微信消息
System.out.println("[WeCom] receivers=" + receivers + ", content=" + content);
return NoticeSendResult.builder()
.success(true)
.receivers(receivers)
.content(content)
.errorMessage(null)
.build();
}
}
@@ -0,0 +1,103 @@
package cn.universal.manager.notice.service.channel;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.json.JSONObject;
import cn.universal.manager.notice.model.NoticeChannel;
import java.util.Map;
import org.springframework.stereotype.Component;
@Component
public class WebhookNoticeSendChannel extends AbstractNoticeSendChannel {
@Override
public boolean support(String type) {
return "webhook".equalsIgnoreCase(type);
}
@Override
public NoticeSendResult send(
String content, String receivers, NoticeChannel config, Map<String, Object> params) {
if (StrUtil.isBlank(content)) {
logSend("Webhook", content, receivers, false, "发送内容为空");
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage("发送内容为空")
.build();
}
JSONObject configObj = parseConfig(config);
String webhookUrl = configObj.getStr("webhook");
String authTokenKey = configObj.getStr("AuthTokenKey");
String tokenValue = configObj.getStr("TokenValue");
if (StrUtil.isBlank(webhookUrl)) {
String errMsg = "Webhook地址未配置";
logSend("Webhook", content, receivers, false, errMsg);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
try {
HttpRequest httpRequest = HttpUtil.createPost(webhookUrl);
httpRequest.timeout(5000); // 默认5秒超时
httpRequest.header(Header.CONTENT_TYPE, "application/json");
// 自定义header
if (StrUtil.isNotBlank(tokenValue)) {
httpRequest.header(
StrUtil.isBlank(authTokenKey) ? Header.AUTHORIZATION.getValue() : authTokenKey,
tokenValue);
}
// 发送内容
httpRequest.body(content);
HttpResponse response = httpRequest.execute();
String result = response.body();
int status = response.getStatus();
logger.warn(
"[Webhook推送] url={}, status={}, body={}, result={}", webhookUrl, status, content, result);
if (status >= 200 && status < 300) {
logSend("Webhook", content, receivers, true, "发送成功");
return NoticeSendResult.builder()
.success(true)
.receivers(receivers)
.content(content)
.errorMessage(null)
.build();
} else {
String errMsg = "Webhook响应码:" + status + ", response=" + result;
logSend("Webhook", content, receivers, false, errMsg);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
} catch (Exception e) {
String errMsg = "Webhook发送异常: " + e.getMessage();
logSend("Webhook", content, receivers, false, errMsg);
return NoticeSendResult.builder()
.success(false)
.receivers(receivers)
.content(content)
.errorMessage(errMsg)
.build();
}
}
private boolean isJson(String str) {
try {
JSONUtil.parse(str);
return true;
} catch (Exception e) {
return false;
}
}
}
@@ -0,0 +1,74 @@
package cn.universal.manager.notice.service.impl;
import cn.universal.manager.notice.mapper.NoticeChannelMapper;
import cn.universal.manager.notice.model.NoticeChannel;
import cn.universal.manager.notice.service.NoticeChannelService;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;
@Service
public class NoticeChannelServiceImpl implements NoticeChannelService {
@Autowired private NoticeChannelMapper noticeChannelMapper;
@Override
public List<NoticeChannel> list() {
return noticeChannelMapper.selectAll();
}
@Override
public List<NoticeChannel> search(String name, String channelType, String status) {
Example example = new Example(NoticeChannel.class);
Example.Criteria criteria = example.createCriteria();
if (name != null && !name.trim().isEmpty()) {
criteria.andLike("name", "%" + name + "%");
}
if (channelType != null && !channelType.trim().isEmpty()) {
criteria.andEqualTo("channelType", channelType);
}
if (status != null && !status.trim().isEmpty()) {
criteria.andEqualTo("status", status);
}
example.orderBy("createTime").desc();
return noticeChannelMapper.selectByExample(example);
}
@Override
public void save(NoticeChannel channel) {
Date now = new Date();
if (channel.getId() == null) {
// 新增
channel.setCreateTime(now);
channel.setUpdateTime(now);
noticeChannelMapper.insert(channel);
} else {
// 更新
channel.setUpdateTime(now);
noticeChannelMapper.updateByPrimaryKeySelective(channel);
}
}
@Override
public void delete(Long id) {
noticeChannelMapper.deleteByPrimaryKey(id);
}
@Override
public void deleteBatch(List<Long> ids) {
Example example = new Example(NoticeChannel.class);
example.createCriteria().andIn("id", ids);
noticeChannelMapper.deleteByExample(example);
}
@Override
public NoticeChannel getById(Long id) {
return noticeChannelMapper.selectByPrimaryKey(id);
}
}
@@ -0,0 +1,46 @@
package cn.universal.manager.notice.service.impl;
import cn.hutool.core.util.StrUtil;
import cn.universal.manager.notice.mapper.NoticeSendRecordMapper;
import cn.universal.manager.notice.model.NoticeSendRecord;
import cn.universal.manager.notice.service.NoticeRecordService;
import java.util.List;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;
@Service
public class NoticeRecordServiceImpl implements NoticeRecordService {
private final NoticeSendRecordMapper recordMapper;
public NoticeRecordServiceImpl(NoticeSendRecordMapper recordMapper) {
this.recordMapper = recordMapper;
}
@Override
public void save(NoticeSendRecord record) {
record.setSendTime(new java.util.Date());
recordMapper.insertSelective(record);
}
@Override
public List<NoticeSendRecord> list() {
return recordMapper.selectAll();
}
@Override
public List<NoticeSendRecord> search(String keyword, String type, String status) {
Example example = new Example(NoticeSendRecord.class);
Example.Criteria c = example.createCriteria();
if (StrUtil.isNotEmpty(keyword)) {
c.andLike("receivers", "%" + keyword + "%");
}
if (StrUtil.isNotEmpty(type)) {
c.andEqualTo("status", type);
}
if (StrUtil.isNotEmpty(status)) {
c.andEqualTo("status", status);
}
return recordMapper.selectByExample(example);
}
}
@@ -0,0 +1,169 @@
package cn.universal.manager.notice.service.impl;
import cn.universal.manager.notice.model.NoticeChannel;
import cn.universal.manager.notice.model.NoticeSendRecord;
import cn.universal.manager.notice.model.NoticeSendRequest;
import cn.universal.manager.notice.model.NoticeTemplate;
import cn.universal.manager.notice.service.NoticeChannelService;
import cn.universal.manager.notice.service.NoticeRecordService;
import cn.universal.manager.notice.service.NoticeService;
import cn.universal.manager.notice.service.NoticeTemplateService;
import cn.universal.manager.notice.service.channel.NoticeSendChannel;
import cn.universal.manager.notice.service.channel.NoticeSendResult;
import cn.universal.manager.notice.util.TemplateUtil;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@Service
@Slf4j
public class NoticeServiceImpl implements NoticeService {
private final NoticeTemplateService templateService;
private final NoticeRecordService recordService;
private final NoticeChannelService noticeChannelService;
private final List<NoticeSendChannel> channelList;
public NoticeServiceImpl(
NoticeTemplateService templateService,
NoticeRecordService recordService,
NoticeChannelService noticeChannelService,
List<NoticeSendChannel> channelList) {
this.templateService = templateService;
this.recordService = recordService;
this.noticeChannelService = noticeChannelService;
this.channelList = channelList;
}
private String mergeReceivers(
String templateReceivers, String requestReceivers, Map<String, Object> params) {
String tpl = TemplateUtil.replaceParams(templateReceivers, params);
String req = TemplateUtil.replaceParams(requestReceivers, params);
if (!StringUtils.hasText(tpl)) {
return req;
}
if (!StringUtils.hasText(req)) {
return tpl;
}
Set<String> set = new LinkedHashSet<>();
set.addAll(Arrays.asList(tpl.split(",")));
set.addAll(Arrays.asList(req.split(",")));
return String.join(",", set);
}
private static class SendContext {
NoticeTemplate template;
NoticeChannel channelConfig;
NoticeSendChannel channel;
String content;
String mergedReceivers;
}
private SendContext validateAndPrepare(NoticeSendRequest request) {
if (request == null || request.getTemplateId() == null) {
throw new IllegalArgumentException("模板ID不能为空");
}
NoticeTemplate template = templateService.getById(request.getTemplateId());
if (template == null) {
throw new IllegalArgumentException("通知模板不存在");
}
if (!StringUtils.hasText(template.getContent())) {
throw new IllegalArgumentException("模板内容不能为空");
}
NoticeChannel channelConfig = noticeChannelService.getById(template.getChannelId());
if (channelConfig == null) {
throw new IllegalArgumentException("渠道配置不存在");
}
NoticeSendChannel channel =
channelList.stream()
.filter(c -> c.support(channelConfig.getChannelType()))
.findFirst()
.orElseThrow(
() -> new IllegalArgumentException("不支持的通知类型:" + channelConfig.getChannelType()));
String content = TemplateUtil.replaceParams(template.getContent(), request.getParams());
String mergedReceivers =
mergeReceivers(template.getReceivers(), request.getReceivers(), request.getParams());
if (!StringUtils.hasText(mergedReceivers)) {
// throw new IllegalArgumentException("收件人为空");
log.debug("推送消息渠道={},收件人为空,部分不需要收件人", request.getTemplateId());
}
SendContext ctx = new SendContext();
ctx.template = template;
ctx.channelConfig = channelConfig;
ctx.channel = channel;
ctx.content = content;
ctx.mergedReceivers = mergedReceivers;
return ctx;
}
private NoticeSendRecord buildRecord(SendContext ctx, NoticeSendRequest request) {
NoticeSendRecord record = new NoticeSendRecord();
record.setTemplateId(ctx.template.getId());
record.setConfigId(ctx.channelConfig.getId());
record.setReceivers(ctx.mergedReceivers);
record.setParams(request.getParams() != null ? request.getParams().toString() : null);
record.setSendTime(new Date());
return record;
}
@Override
public void send(NoticeSendRequest request) {
SendContext ctx;
try {
ctx = validateAndPrepare(request);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
NoticeSendRecord record = buildRecord(ctx, request);
try {
NoticeSendResult result =
ctx.channel.send(
ctx.content, ctx.mergedReceivers, ctx.channelConfig, request.getParams());
record.setStatus(result.isSuccess() ? "SUCCESS" : "FAIL");
record.setResult(result.getErrorMessage() == null ? "OK" : result.getErrorMessage());
if (!result.isSuccess()) {
throw new RuntimeException("消息推送失败: " + result.getErrorMessage());
}
} finally {
recordService.save(record);
}
}
@Override
public NoticeSendResult sendR(NoticeSendRequest request) {
SendContext ctx;
try {
ctx = validateAndPrepare(request);
} catch (Exception e) {
return NoticeSendResult.builder().success(false).errorMessage(e.getMessage()).build();
}
NoticeSendRecord record = buildRecord(ctx, request);
NoticeSendResult result;
try {
result =
ctx.channel.send(
ctx.content, ctx.mergedReceivers, ctx.channelConfig, request.getParams());
record.setStatus(result.isSuccess() ? "SUCCESS" : "FAIL");
record.setResult(result.getErrorMessage() == null ? "OK" : result.getErrorMessage());
} catch (Exception e) {
record.setStatus("FAIL");
record.setResult(e.getMessage());
log.error("推送消息失败", e);
result =
NoticeSendResult.builder()
.success(false)
.errorMessage("消息推送失败:" + e.getMessage())
.build();
} finally {
recordService.save(record);
}
return result;
}
}
@@ -0,0 +1,155 @@
package cn.universal.manager.notice.service.impl;
import cn.hutool.core.util.StrUtil;
import cn.universal.manager.notice.mapper.NoticeTemplateMapper;
import cn.universal.manager.notice.model.NoticeChannel;
import cn.universal.manager.notice.model.NoticeTemplate;
import cn.universal.manager.notice.service.NoticeChannelService;
import cn.universal.manager.notice.service.NoticeTemplateService;
import cn.universal.manager.notice.service.channel.NoticeChannelFactory;
import cn.universal.manager.notice.service.channel.NoticeSendChannel;
import cn.universal.manager.notice.util.TemplateUtil;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;
@Service
public class NoticeTemplateServiceImpl implements NoticeTemplateService {
private final NoticeTemplateMapper templateMapper;
private final NoticeChannelService noticeChannelService;
private final NoticeChannelFactory channelFactory;
public NoticeTemplateServiceImpl(
NoticeTemplateMapper templateMapper,
NoticeChannelService noticeChannelService,
NoticeChannelFactory channelFactory) {
this.templateMapper = templateMapper;
this.noticeChannelService = noticeChannelService;
this.channelFactory = channelFactory;
}
@Override
public List<NoticeTemplate> list() {
return templateMapper.selectAll();
}
@Override
public void save(NoticeTemplate template) {
Date now = new Date();
if (template.getId() == null) {
// 新增
template.setCreateTime(now);
template.setUpdateTime(now);
templateMapper.insertSelective(template);
} else {
// 更新
template.setUpdateTime(now);
templateMapper.updateByPrimaryKeySelective(template);
}
}
@Override
public void delete(Long id) {
templateMapper.deleteByPrimaryKey(id);
}
@Override
public NoticeTemplate getById(Long id) {
return templateMapper.selectByPrimaryKey(id);
}
@Override
public List<NoticeTemplate> search(String name, String channelType, String status) {
Example example = new Example(NoticeTemplate.class);
Example.Criteria c = example.createCriteria();
if (StrUtil.isNotEmpty(name)) {
c.andLike("name", "%" + name + "%");
}
if (StrUtil.isNotEmpty(channelType)) {
c.andEqualTo("channelType", channelType);
}
if (StrUtil.isNotEmpty(status)) {
c.andEqualTo("status", status);
}
example.orderBy("createTime").desc();
return templateMapper.selectByExample(example);
}
@Override
public void deleteBatch(List<Long> ids) {
Example example = new Example(NoticeTemplate.class);
example.createCriteria().andIn("id", ids);
templateMapper.deleteByExample(example);
}
@Override
public void enableBatch(List<Long> ids) {
if (ids == null || ids.isEmpty()) {
return;
}
Example example = new Example(NoticeTemplate.class);
example.createCriteria().andIn("id", ids);
NoticeTemplate update = new NoticeTemplate();
update.setStatus("1"); // 启用
templateMapper.updateByExampleSelective(update, example);
}
@Override
public void disableBatch(List<Long> ids) {
if (ids == null || ids.isEmpty()) {
return;
}
Example example = new Example(NoticeTemplate.class);
example.createCriteria().andIn("id", ids);
NoticeTemplate update = new NoticeTemplate();
update.setStatus("0"); // 停用
templateMapper.updateByExampleSelective(update, example);
}
@Override
public void testTemplate(Long templateId, String receivers, Object params) {
// 1. 获取模板
NoticeTemplate template = getById(templateId);
if (template == null) {
throw new IllegalArgumentException("模板不存在");
}
// 2. 获取渠道配置
NoticeChannel channelConfig = noticeChannelService.getById(template.getChannelId());
if (channelConfig == null) {
throw new IllegalArgumentException("渠道配置不存在");
}
// 3. 替换参数
Map<String, Object> paramMap = null;
if (params instanceof Map) {
paramMap = (Map<String, Object>) params;
} else {
paramMap = TemplateUtil.parseJson(TemplateUtil.toJson(params));
}
String content = TemplateUtil.replaceParams(template.getContent(), paramMap);
// 4. 获取对应的通知渠道
NoticeSendChannel channel =
channelFactory
.getChannel(template.getChannelType())
.orElseThrow(
() -> new IllegalArgumentException("不支持的渠道类型: " + template.getChannelType()));
// 5. 发送测试消息
try {
channel.send(content, receivers, channelConfig, paramMap);
System.out.println(
"✅ 测试发送成功 - 模板: " + template.getName() + ", 渠道: " + template.getChannelType());
} catch (Exception e) {
System.err.println(
"❌ 测试发送失败 - 模板: " + template.getName() + ", 渠道: " + template.getChannelType());
System.err.println("错误信息: " + e.getMessage());
throw new RuntimeException("测试发送失败", e);
}
}
}
@@ -0,0 +1,104 @@
package cn.universal.manager.notice.util;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import java.util.Iterator;
import java.util.Map;
/** JSON脱敏工具类 用于检测和脱敏包含Secret关键字的字段 */
public class JsonDesensitizationUtil {
private static final String SECRET_KEYWORD = "secret";
private static final String MASK_VALUE = "******";
/**
* 对JSON对象进行脱敏处理
*
* @param jsonString JSON字符串
* @return 脱敏后的JSON对象
*/
public static JSONObject desensitize(String jsonString) {
if (jsonString == null || jsonString.trim().isEmpty()) {
return null;
}
try {
JSONObject jsonObject = JSONUtil.parseObj(jsonString);
return desensitizeJsonObject(jsonObject);
} catch (Exception e) {
return null;
}
}
/**
* 对JSONObject进行脱敏处理
*
* @param jsonObject JSON对象
* @return 脱敏后的JSON对象
*/
public static JSONObject desensitizeJsonObject(JSONObject jsonObject) {
if (jsonObject == null) {
return null;
}
JSONObject result = new JSONObject();
Iterator<Map.Entry<String, Object>> iterator = jsonObject.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Object> entry = iterator.next();
String key = entry.getKey();
Object value = entry.getValue();
// 检查key是否包含Secret关键字(忽略大小写)
if (containsSecretKeyword(key)) {
// 脱敏处理
result.put(key, MASK_VALUE);
} else if (value instanceof JSONObject) {
// 递归处理嵌套的JSONObject
result.put(key, desensitizeJsonObject((JSONObject) value));
} else {
// 直接复制其他值
result.put(key, value);
}
}
return result;
}
/**
* 检查字符串是否包含Secret关键字(忽略大小写)
*
* @param text 要检查的字符串
* @return 是否包含Secret关键字
*/
private static boolean containsSecretKeyword(String text) {
if (text == null) {
return false;
}
return text.toLowerCase().contains(SECRET_KEYWORD);
}
/**
* 将Object转换为JSONObject并进行脱敏处理
*
* @param obj 要处理的对象
* @return 脱敏后的JSONObject
*/
public static JSONObject desensitizeObject(Object obj) {
if (obj == null) {
return null;
}
try {
if (obj instanceof JSONObject) {
return desensitizeJsonObject((JSONObject) obj);
} else {
// 先转换为JSON字符串,再解析为JSONObject
String jsonString = JSONUtil.toJsonStr(obj);
return desensitize(jsonString);
}
} catch (Exception e) {
return null;
}
}
}
@@ -0,0 +1,53 @@
package cn.universal.manager.notice.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TemplateUtil {
// 支持 #xxx 和 #{xxx} 两种格式(全部用普通字符串,不用字符串模板)
private static final Pattern PARAM_PATTERN =
Pattern.compile("#([a-zA-Z0-9_]+)|#\\{([a-zA-Z0-9_]+)\\}");
private static final ObjectMapper objectMapper = new ObjectMapper();
/** 替换模板中的参数,兼容 #xxx 和 #{xxx} 两种格式 */
public static String replaceParams(String template, Map<String, Object> params) {
if (template == null || params == null) {
return template;
}
Matcher matcher = PARAM_PATTERN.matcher(template);
StringBuffer result = new StringBuffer();
while (matcher.find()) {
String paramName = matcher.group(1) != null ? matcher.group(1) : matcher.group(2);
Object value = params.get(paramName);
String replacement = value != null ? value.toString() : "";
matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
}
matcher.appendTail(result);
return result.toString();
}
/** 解析JSON字符串为Map */
@SuppressWarnings("unchecked")
public static Map<String, Object> parseJson(String json) {
try {
if (json == null || json.trim().isEmpty()) {
return Map.of();
}
return objectMapper.readValue(json, Map.class);
} catch (Exception e) {
return Map.of();
}
}
/** 将对象转换为JSON字符串 */
public static String toJson(Object obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (Exception e) {
return "{}";
}
}
}
@@ -0,0 +1,109 @@
// package cn.universal.manager.notice;
//
// import cn.universal.manager.notice.model.NoticeSendChannel;
// import cn.universal.manager.notice.model.NoticeTemplate;
// import cn.universal.manager.notice.service.NoticeChannelService;
// import cn.universal.manager.notice.service.NoticeTemplateService;
// import cn.universal.manager.notice.util.TemplateUtil;
// import org.junit.jupiter.api.Test;
// import org.springframework.beans.factory.annotation.Autowired;
// import org.springframework.boot.test.context.SpringBootTest;
//
// import java.util.HashMap;
// import java.util.List;
// import java.util.Map;
//
// @SpringBootTest
// public class NoticeTest {
//
// @Autowired
// private NoticeChannelService noticeChannelService;
//
// @Autowired
// private NoticeTemplateService noticeTemplateService;
//
// @Test
// public void testChannelCRUD() {
// // 测试渠道CRUD操作
// NoticeSendChannel channel = new NoticeSendChannel();
// channel.setName("测试钉钉机器人");
// channel.setChannelType("dingTalk");
// channel.setConfig("{\"webhook\":\"https://test.com\",\"secret\":\"test\"}");
// channel.setStatus("1");
// channel.setRemark("测试渠道");
// channel.setCreator("test");
//
// // 保存
// noticeChannelService.save(channel);
// System.out.println("保存渠道成功,ID: " + channel.getId());
//
// // 查询
// NoticeSendChannel saved = noticeChannelService.getById(channel.getId());
// System.out.println("查询渠道: " + saved.getName());
//
// // 搜索
// List<NoticeSendChannel> list = noticeChannelService.search("测试", "dingTalk", "1");
// System.out.println("搜索到 " + list.size() + " 个渠道");
//
// // 删除
// noticeChannelService.delete(channel.getId());
// System.out.println("删除渠道成功");
// }
//
// @Test
// public void testTemplateCRUD() {
// // 测试模板CRUD操作
// NoticeTemplate template = new NoticeTemplate();
// template.setName("测试告警模板");
// template.setChannelType("dingTalk");
// template.setChannelId(1L);
// template.setContent("设备${deviceName}发生${alertLevel}级别告警:${alertMessage}");
// template.setReceivers("[\"test_group\"]");
// template.setStatus("1");
// template.setRemark("测试模板");
// template.setCreator("test");
//
// // 保存
// noticeTemplateService.save(template);
// System.out.println("保存模板成功,ID: " + template.getId());
//
// // 查询
// NoticeTemplate saved = noticeTemplateService.getById(template.getId());
// System.out.println("查询模板: " + saved.getName());
//
// // 搜索
// List<NoticeTemplate> list = noticeTemplateService.search("测试", "dingTalk", "1");
// System.out.println("搜索到 " + list.size() + " 个模板");
//
// // 删除
// noticeTemplateService.delete(template.getId());
// System.out.println("删除模板成功");
// }
//
// @Test
// public void testTemplateUtil() {
// // 测试模板工具类
// String template = "设备${deviceName}发生${alertLevel}级别告警:${alertMessage},时间:${alertTime}";
//
// Map<String, Object> params = new HashMap<>();
// params.put("deviceName", "温度传感器001");
// params.put("alertLevel", "严重");
// params.put("alertMessage", "温度过高报警");
// params.put("alertTime", "2024-01-15 14:30:25");
//
// String result = TemplateUtil.replaceParams(template, params);
// System.out.println("模板替换结果: " + result);
// }
//
// @Test
// public void testTemplateTest() {
// // 测试模板测试功能
// Map<String, Object> params = new HashMap<>();
// params.put("deviceName", "温度传感器001");
// params.put("alertLevel", "严重");
// params.put("alertMessage", "温度过高报警");
// params.put("alertTime", "2024-01-15 14:30:25");
//
// noticeTemplateService.testTemplate(1L, "test_group", params);
// }
// }
@@ -0,0 +1,133 @@
/*
*
* Copyright (c) 2025, IoT-Universal. All Rights Reserved.
*
* @Description: 七牛云存储实现
*
*/
package cn.universal.ossm.oss.service.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.universal.common.exception.IoTException;
import cn.universal.ossm.oss.entity.UploadResult;
import cn.universal.ossm.oss.properties.CloudStorageProperties;
import cn.universal.ossm.oss.service.ICloudStorageService;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import org.springframework.stereotype.Service;
@Service
public class QiniuCloudStorageServiceImpl implements ICloudStorageService {
@Resource private CloudStorageProperties properties;
private UploadManager uploadManager;
@PostConstruct
public void init() {
CloudStorageProperties.QiniuProperties cfg = properties.getQiniu();
Region region = Region.autoRegion();
Configuration configuration = new Configuration(region);
this.uploadManager = new UploadManager(configuration);
}
@Override
public void createBucket() {}
@Override
public String getServiceType() {
return "qiniu";
}
@Override
public String getPath(String prefix, String suffix) {
StringBuilder path = new StringBuilder();
if (StrUtil.isNotBlank(prefix)) {
path.append(prefix);
}
path.append(System.currentTimeMillis());
if (StrUtil.isNotBlank(suffix)) {
path.append(suffix);
}
return path.toString();
}
private String getUpToken() {
CloudStorageProperties.QiniuProperties cfg = properties.getQiniu();
Auth auth = Auth.create(cfg.getAccessKey(), cfg.getSecretKey());
StringMap putPolicy = new StringMap();
return auth.uploadToken(cfg.getBucketName(), null, 3600, putPolicy);
}
private String buildUrl(String key) {
CloudStorageProperties.QiniuProperties cfg = properties.getQiniu();
String domain = cfg.getDomain();
if (Boolean.TRUE.equals(cfg.getIsHttps())) {
domain = domain.replaceFirst("^http://", "https://");
}
if (!domain.startsWith("http")) {
domain = (Boolean.TRUE.equals(cfg.getIsHttps()) ? "https://" : "http://") + domain;
}
return domain + "/" + key;
}
@Override
public UploadResult upload(byte[] data, String path, String contentType) {
try {
String token = getUpToken();
String key = path;
com.qiniu.http.Response response =
uploadManager.put(new ByteArrayInputStream(data), key, token, null, contentType);
if (!response.isOK()) {
throw new IoTException("七牛上传失败:" + response.toString());
}
DefaultPutRet putRet = JSONUtil.toBean(response.bodyString(), DefaultPutRet.class);
String url = buildUrl(putRet.key);
return new UploadResult().setUrl(url).setFilename(putRet.key);
} catch (Exception e) {
throw new IoTException("七牛上传异常", e);
}
}
@Override
public void delete(String path) {
// 可按需实现:调用 BucketManager 删除
}
@Override
public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
String path = getPath(properties.getQiniu().getPrefix(), suffix);
return upload(data, path, contentType);
}
@Override
public UploadResult upload(InputStream inputStream, String path, String contentType) {
try {
byte[] bytes = inputStream.readAllBytes();
return upload(bytes, path, contentType);
} catch (Exception e) {
throw new IoTException("七牛上传异常", e);
}
}
@Override
public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
String path = getPath(properties.getQiniu().getPrefix(), suffix);
return upload(inputStream, path, contentType);
}
@Override
public String getEndpointLink() {
return properties.getQiniu().getDomain();
}
}
@@ -167,8 +167,11 @@ public class DefaultSecurityConfig {
.permitAll()
.requestMatchers("/emqx/**")
.permitAll()
.requestMatchers("/ct/aiot/**")
.permitAll()
// 测试接口
.requestMatchers("/test/**")
.permitAll()
.requestMatchers("/monitor/**")
.permitAll()
@@ -227,6 +230,8 @@ public class DefaultSecurityConfig {
// 测试和监控点
.requestMatchers("/test/**", "/monitor/**")
.permitAll()
.requestMatchers("/ct/aiot/**")
.permitAll()
// 前端相关
.requestMatchers(
"/js/**",
@@ -30,22 +30,17 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class CommonRequest {
/**
* http请求超时时间
*/
/** http请求超时时间 */
private static final Integer HTTP_TIME_OUT = 1200;
/**
* URL状态信息
*/
/** URL状态信息 */
public static class UrlStatus {
private int failureCount = 0;
private long lastFailureTime = 0;
private long nextAllowTime = 0;
public UrlStatus() {
}
public UrlStatus() {}
public int getFailureCount() {
return failureCount;
@@ -75,25 +70,19 @@ public class CommonRequest {
private static String CACHE_NOTICE = "DNotice:";
// 存储URL状态信息,30分钟后过期
private static Cache<String, UrlStatus> urlStatusCache = Caffeine.newBuilder()
.expireAfterWrite(30, TimeUnit.MINUTES).maximumSize(256).build();
private static Cache<String, UrlStatus> urlStatusCache =
Caffeine.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).maximumSize(256).build();
/**
* 分级阈值配置
*/
/** 分级阈值配置 */
private static final int LEVEL_1_THRESHOLD = 3; // 轻微故障阈值
private static final int LEVEL_2_THRESHOLD = 6; // 中等故障阈值
private static final int LEVEL_3_THRESHOLD = 10; // 严重故障阈值
/**
* 基础延迟时间(秒)
*/
/** 基础延迟时间(秒) */
private static final int BASE_DELAY_SECONDS = 30;
/**
* 最大延迟时间(分钟)
*/
/** 最大延迟时间(分钟) */
private static final int MAX_DELAY_MINUTES = 10;
private static ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
@@ -115,8 +104,7 @@ public class CommonRequest {
if (isUrlBlocked(url)) {
UrlStatus status = urlStatusCache.getIfPresent(url);
long remainingSeconds = (status.getNextAllowTime() - System.currentTimeMillis()) / 1000;
log.warn("当前地址[{}]因连续失败{}次,暂时限制访问,还需等待{}秒", url,
status.getFailureCount(), remainingSeconds);
log.warn("当前地址[{}]因连续失败{}次,暂时限制访问,还需等待{}秒", url, status.getFailureCount(), remainingSeconds);
return null;
}
@@ -124,7 +112,7 @@ public class CommonRequest {
HttpRequest request = HttpUtil.createPost(url);
request.timeout(HTTP_TIME_OUT);
String timestamp = String.valueOf(System.currentTimeMillis());
String signature = String.valueOf((timestamp + url).hashCode());
String signature = MD5.create().digestHex16((timestamp + body));
request.header("X-Timestamp", timestamp);
request.header("X-Signature", signature);
request.header(Header.CONTENT_TYPE, "application/json");
@@ -146,9 +134,7 @@ public class CommonRequest {
return null;
}
/**
* 检查URL是否被阻止
*/
/** 检查URL是否被阻止 */
private static boolean isUrlBlocked(String url) {
UrlStatus status = urlStatusCache.getIfPresent(url);
if (status == null) {
@@ -159,9 +145,7 @@ public class CommonRequest {
return currentTime < status.getNextAllowTime();
}
/**
* 处理请求成功
*/
/** 处理请求成功 */
private static void handleSuccess(String url) {
UrlStatus status = urlStatusCache.getIfPresent(url);
if (status != null) {
@@ -172,9 +156,7 @@ public class CommonRequest {
}
}
/**
* 处理请求失败 - 智能退避策略
*/
/** 处理请求失败 - 智能退避策略 */
private static void handleFailure(String url) {
UrlStatus status = urlStatusCache.getIfPresent(url);
if (status == null) {
@@ -190,8 +172,12 @@ public class CommonRequest {
status.setNextAllowTime(System.currentTimeMillis() + delayMillis);
String delayInfo = formatDelayInfo(delayMillis);
log.warn("URL[{}]第{}次失败,采用{}策略,下次可访问时间:{}", url, failureCount,
getStrategyLevel(failureCount), delayInfo);
log.warn(
"URL[{}]第{}次失败,采用{}策略,下次可访问时间:{}",
url,
failureCount,
getStrategyLevel(failureCount),
delayInfo);
// 严重故障时发送通知
if (failureCount >= LEVEL_3_THRESHOLD) {
@@ -199,9 +185,7 @@ public class CommonRequest {
}
}
/**
* 计算延迟时间 - 指数退避算法
*/
/** 计算延迟时间 - 指数退避算法 */
private static long calculateDelay(int failureCount) {
if (failureCount <= 1) {
return 0; // 首次失败不延迟
@@ -226,9 +210,7 @@ public class CommonRequest {
return delaySeconds * 1000; // 转换为毫秒
}
/**
* 获取策略级别描述
*/
/** 获取策略级别描述 */
private static String getStrategyLevel(int failureCount) {
if (failureCount <= LEVEL_1_THRESHOLD) {
return "轻微故障-线性退避";
@@ -239,9 +221,7 @@ public class CommonRequest {
}
}
/**
* 格式化延迟信息
*/
/** 格式化延迟信息 */
private static String formatDelayInfo(long delayMillis) {
long seconds = delayMillis / 1000;
if (seconds < 60) {
@@ -257,16 +237,15 @@ public class CommonRequest {
}
}
/**
* 发送失败通知
*/
/** 发送失败通知 */
private static void sendFailureNotification(String url, int failureCount) {
String noticeKey = CACHE_NOTICE + url;
UrlStatus noticeStatus = urlStatusCache.getIfPresent(noticeKey);
// 每小时最多发送一次通知
if (noticeStatus == null || (System.currentTimeMillis() - noticeStatus.getLastFailureTime())
> TimeUnit.HOURS.toMillis(1)) {
if (noticeStatus == null
|| (System.currentTimeMillis() - noticeStatus.getLastFailureTime())
> TimeUnit.HOURS.toMillis(1)) {
log.error("严重故障警告:URL[{}]连续失败{}次,已进入长时间限制状态", url, failureCount);
@@ -276,6 +255,7 @@ public class CommonRequest {
urlStatusCache.put(noticeKey, newNoticeStatus);
// TODO: 集成钉钉/企微等通知
// DingTalkUtil.send("严重故障:地址 " + url + " 连续失败 " + failureCount + " 次");
}
}
@@ -178,7 +178,7 @@ public abstract class IoTDownAdapter<T extends BaseDownRequest> {
.createTime(System.currentTimeMillis() / 1000)
.deviceName(downRequest.getData().getStr("deviceName", downRequest.getDeviceId()))
.state(DeviceStatus.offline.getCode())
.iotId(IdUtil.simpleUUID())
.iotId(downRequest.getProductKey()+downRequest.getDeviceId())
.application(downRequest.getApplicationId())
.creatorId(downRequest.getAppUnionId())
.productName(downRequest.getIoTProduct().getName())
@@ -17,8 +17,7 @@ import lombok.NoArgsConstructor;
@Builder
public class IoTCertificate implements Serializable {
@Id
private Long id;
@Id private Long id;
@Column(name = "ssl_key")
private String sslKey;
@@ -32,51 +32,35 @@ public class IoTDashboardStatistics implements Serializable {
@KeySql(useGeneratedKeys = true)
private Long id;
/**
* 统计日期
*/
/** 统计日期 */
@Column(name = "stat_date")
private LocalDate statDate;
/**
* 产品Key,NULL表示全产品
*/
/** 产品Key,NULL表示全产品 */
@Column(name = "product_key")
private String productKey;
/**
* 推送渠道,NULL表示全渠道
*/
/** 推送渠道,NULL表示全渠道 */
@Column(name = "channel")
private String channel;
/**
* 指标类型
*/
/** 指标类型 */
@Column(name = "metric_type")
private String metricType;
/**
* 指标值
*/
/** 指标值 */
@Column(name = "metric_value")
private Long metricValue;
/**
* 创建时间
*/
/** 创建时间 */
@Column(name = "create_time")
private LocalDateTime createTime;
/**
* 更新时间
*/
/** 更新时间 */
@Column(name = "update_time")
private LocalDateTime updateTime;
/**
* 指标类型枚举
*/
/** 指标类型枚举 */
public enum MetricType {
DEVICE_TOTAL("device_total", "设备总数"),
DEVICE_ONLINE("device_online", "在线设备数"),
@@ -33,90 +33,63 @@ import lombok.NoArgsConstructor;
public class IoTDevice implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long id;
@Id private Long id;
/**
* 对外设备唯一标识符
*/
/** 对外设备唯一标识符 */
@Column(name = "iot_id")
private String iotId;
/**
* 设备自身序号
*/
/** 设备自身序号 */
@Excel(name = "设备序列号")
@Column(name = "device_id")
private String deviceId;
/**
* 实例名称
*/
/** 实例名称 */
// @Excel(name = "实例名称")
@Column(name = "instance")
private String instance;
/**
* 归属应用
*/
/** 归属应用 */
@Column(name = "application")
private String application;
/**
* 激活时间
*/
/** 激活时间 */
@Column(name = "registry_time")
private Integer registryTime;
/**
* 最后上线时间
*/
/** 最后上线时间 */
@Column(name = "online_time")
private Long onlineTime;
/**
* 第三方设备ID唯一标识符
*/
/** 第三方设备ID唯一标识符 */
@Column(name = "ext_device_id")
private String extDeviceId;
/**
* 别名
*/
/** 别名 */
@Column(name = "nick_name")
private String nickName;
/**
* 设备名称
*/
/** 设备名称 */
@Column(name = "product_name")
private String productName;
private Long features;
/**
* 网关产品ProductKey
*/
/** 网关产品ProductKey */
@Excel(name = "网关产品ProductKey")
@Column(name = "gw_product_key")
private String gwProductKey;
/**
* 设备密钥
*/
/** 设备密钥 */
@Excel(name = "设备密钥")
@Column(name = "device_secret")
private String deviceSecret;
/**
* 产品key
*/
/** 产品key */
@Column(name = "product_key")
private String productKey;
/**
* 设备实例名称
*/
/** 设备实例名称 */
@Excel(name = "设备名称")
@Column(name = "device_name")
private String deviceName;
@@ -127,68 +100,46 @@ public class IoTDevice implements Serializable {
@Column(name = "creator_name")
private String creatorName;
/**
* 0-离线,1-在线
*/
/** 0-离线,1-在线 */
@Excel(name = "在线状态")
private Boolean state;
/**
* 说明
*/
/** 说明 */
@Excel(name = "备注")
private String detail;
@Column(name = "create_time")
private Long createTime;
/**
* 派生元数据,有的设备的属性,功能,事件可能会动态的添加
*/
/** 派生元数据,有的设备的属性,功能,事件可能会动态的添加 */
@Column(name = "derive_metadata")
private String deriveMetadata;
/**
* 其他配置
*/
/** 其他配置 */
@Column(name = "configuration")
private String configuration;
/**
* 区域ID
*/
/** 区域ID */
private String areasId;
/**
* 坐标
*/
/** 坐标 */
// @Excel(name = "设备坐标")
private String coordinate;
@Transient
private String deviceNode;
@Transient private String deviceNode;
/**
* 纬度
*/
/** 纬度 */
@Excel(name = "纬度")
private transient String latitude;
/**
* 经度
*/
/** 经度 */
@Excel(name = "经度")
private transient String longitude;
/**
* 请求参数
*/
@Builder.Default
private Map<String, Object> params = new HashMap<>();
/** 请求参数 */
@Builder.Default private Map<String, Object> params = new HashMap<>();
/**
* 接收额外参数
*/
/** 接收额外参数 */
@Excel(name = "其他配置")
@Builder.Default
private Map<String, Object> otherParams = new HashMap<>();
@@ -20,34 +20,22 @@ import lombok.Data;
@Builder
public class IoTDeviceEvents implements Serializable {
/**
* 事件标识
*/
/** 事件标识 */
private String id;
/**
* 事件名称
*/
/** 事件名称 */
private String name;
/**
* 事件级别
*/
/** 事件级别 */
private String level;
/**
* 描述
*/
/** 描述 */
private String description;
/**
* 事件总数
*/
/** 事件总数 */
private String qty;
/**
* 最新事件上报时间
*/
/** 最新事件上报时间 */
private String time;
// 是否设置存储策略
@@ -33,31 +33,20 @@ import lombok.NoArgsConstructor;
@Builder
public class IoTDeviceFenceRel implements Serializable {
@Id
private Long id;
@Id private Long id;
/**
* 围栏id
*/
/** 围栏id */
private Long fenceId;
/**
* 设备唯一标识符
*/
/** 设备唯一标识符 */
private String iotId;
/**
* 设备序列号
*/
/** 设备序列号 */
private String deviceId;
/**
* 创建人
*/
/** 创建人 */
private String creatorId;
/**
* 创建时间
*/
/** 创建时间 */
private Date createDate;
}
@@ -20,33 +20,21 @@ import lombok.Data;
@Builder
public class IoTDeviceFunction implements Serializable {
/**
* 功能标识
*/
/** 功能标识 */
private String id;
/**
* 功能名称
*/
/** 功能名称 */
private String name;
/**
* 是否是配置
*/
/** 是否是配置 */
private boolean config;
/**
* 描述
*/
/** 描述 */
private String description;
/**
* 功能来源
*/
/** 功能来源 */
private String source;
/**
* 输入
*/
/** 输入 */
private String inputs;
}
@@ -55,8 +55,6 @@ public class IoTDeviceFunctionTask implements Serializable {
private String command;
private String commandData;
/**
* 状态 0.待执行;1.已执行;2.正在执行
*/
/** 状态 0.待执行;1.已执行;2.正在执行 */
private Integer status;
}
@@ -42,86 +42,53 @@ public class IoTDeviceGeoFence implements Serializable {
@KeySql(genId = SQenGenId.class)
private Long id;
/**
* 围栏名称
*/
/** 围栏名称 */
private String name;
/**
* 围栏状态 0.启用 1.停用
*/
/** 围栏状态 0.启用 1.停用 */
private Integer status;
/**
* 触发模式 in.进入 out.离开 all.进入&离开
*/
/** 触发模式 in.进入 out.离开 all.进入&离开 */
private String touchWay;
/**
* 范围
*/
/** 范围 */
private String fence;
/**
* 类型 circle.圆 polygon.多边形
*/
/** 类型 circle.圆 polygon.多边形 */
private String type;
/**
* 圆形中心点
*/
/** 圆形中心点 */
private String point;
/**
* 半径
*/
/** 半径 */
private BigDecimal radius;
/**
* 创建人
*/
/** 创建人 */
private String creatorId;
/**
* 周触发(天)
*/
/** 周触发(天) */
private String weekTime;
/**
* 天触发开始时间(时)
*/
/** 天触发开始时间(时) */
private String beginTime;
/**
* 天触发结束时间(时)
*/
/** 天触发结束时间(时) */
private String endTime;
/**
* 归属第三方应用
*/
/** 归属第三方应用 */
private String creatorUser;
@Transient
private List<String> queryUserList;
@Transient private List<String> queryUserList;
/**
* 创建时间
*/
/** 创建时间 */
private Date createDate;
/**
* 更新时间
*/
/** 更新时间 */
private Date updateDate;
/**
* 不触发时间
*/
/** 不触发时间 */
private String noTriggerTime;
/**
* 延迟时间 分钟
*/
/** 延迟时间 分钟 */
private Integer delayTime;
}
@@ -32,76 +32,52 @@ public class IoTDeviceGroup implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 分组ID,非自增
*/
/** 分组ID,非自增 */
@Id
@KeySql(genId = SQenGenId.class)
private Long id;
/**
* 分组名称
*/
/** 分组名称 */
@Column(name = "group_name")
private String groupName;
/**
* 分组标识
*/
/** 分组标识 */
@Column(name = "group_code")
private String groupCode;
/**
* 群组描述
*/
/** 群组描述 */
@Column(name = "group_describe")
private String groupDescribe;
/**
* 父id
*/
/** 父id */
@Column(name = "parent_id")
private Long parentId;
/**
* 是否有子分组
*/
/** 是否有子分组 */
@Column(name = "has_child")
private Integer hasChild;
/**
* 分组级别
*/
/** 分组级别 */
@Column(name = "group_level")
private Integer groupLevel;
/**
* 激活设备数
*/
/** 激活设备数 */
@Column(name = "relat_dev_count")
private Integer relatDevCount;
/**
* 关联设备树
*/
/** 关联设备树 */
@Column(name = "active_dev_count")
private Integer activeDevCount;
/**
* 创建人
*/
/** 创建人 */
@Column(name = "creator_id")
private String creatorId;
/**
* 实例编号
*/
/** 实例编号 */
@Column(name = "instance")
private String instance;
/**
* 标签
*/
/** 标签 */
@Column(name = "tag")
private String tag;
}
@@ -16,6 +16,7 @@ import jakarta.persistence.Column;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -28,83 +29,47 @@ import lombok.NoArgsConstructor;
@Builder
public class IoTDeviceLog implements Serializable {
@Id
private Long id;
@Id private Long id;
/**
* 唯一编码
*/
@Column(name = "iot_id")
/** 唯一编码 */
@Column(name = "iot_id", length = 128)
private String iotId;
/**
* 设备自身序号
*/
/** 设备自身序号 */
@Column(name = "device_id")
private String deviceId;
/**
* 第三方设备ID唯一标识符
*/
// @Transient
@Column(name = "ext_device_id")
private String extDeviceId;
/**
* 产品ID
*/
/** 产品ID */
@Column(name = "product_key")
private String productKey;
/**
* 设备名称
*/
/** 设备名称 */
@Column(name = "device_name")
private String deviceName;
/**
* 消息类型
*/
/** 消息类型 */
@Column(name = "message_type")
private String messageType;
/**
* 指令ID
*/
/** 指令ID */
@Column(name = "command_id")
private String commandId;
/**
* 指令ID
*/
/** 指令ID */
@Column(name = "command_status")
private Integer commandStatus;
/**
* 创建人
*/
@Column(name = "create_id")
private String createId;
/**
* 事件名称
*/
/** 事件名称 */
private String event;
/**
* 实例名称
*/
private String instance;
/**
* 创建时间
*/
/** 创建时间 */
@Column(name = "create_time")
private Long createTime;
private LocalDateTime createTime;
/**
* 内容
*/
/** 内容 */
private String content;
private String point;
@@ -16,6 +16,7 @@ import jakarta.persistence.Column;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -28,38 +29,30 @@ import lombok.NoArgsConstructor;
@Builder
public class IoTDeviceLogMetadata implements Serializable {
@Id
private Long id;
@Id private Long id;
@Column(name = "iot_id")
private String iotId;
/**
* 产品唯一标识
*/
/** 产品唯一标识 */
@Column(name = "product_key")
private String productKey;
/**
* 设备名称
*/
@Column(name = "device_name")
/** 设备名称 */
@Column(name = "device_name", length = 64)
private String deviceName;
@Column(name = "device_id")
@Column(name = "device_id", length = 64)
private String deviceId;
/**
* 消息类型
*/
/** 消息类型 */
@Column(name = "message_type")
private String messageType;
@Column(length = 32)
private String event;
/**
* 属性
*/
/** 属性 */
private String property;
private String ext1;
@@ -68,15 +61,11 @@ public class IoTDeviceLogMetadata implements Serializable {
private String ext3;
/**
* 发生时间
*/
/** 发生时间 */
@Column(name = "create_time")
private Integer createTime;
private LocalDateTime createTime;
/**
* 其他
*/
/** 其他 */
private String content;
private static final long serialVersionUID = 1L;
@@ -22,43 +22,27 @@ public class IoTDeviceProperties implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 属性标识
*/
/** 属性标识 */
private String id;
/**
* 属性名称
*/
/** 属性名称 */
private String name;
/**
* 数据类型
*/
/** 数据类型 */
private String type;
/**
* 属性值来源
*/
/** 属性值来源 */
private String source;
/**
* 单位
*/
/** 单位 */
private String unit;
/**
* 枚举键值
*/
/** 枚举键值 */
private String elements;
/**
* 描述
*/
/** 描述 */
private String description;
/**
* 读写
*/
/** 读写 */
private String mode;
}
@@ -36,8 +36,7 @@ public class IoTDeviceProtocol implements Serializable {
private String name;
private String description;
private Byte state;
@Id
private String id;
@Id private String id;
private String type;
private String configuration;
private String example;
@@ -29,36 +29,25 @@ import lombok.NoArgsConstructor;
@Builder
public class IoTDeviceRuleLog implements Serializable {
@Id
private Long id;
@Id private Long id;
/**
* 业务ID
*/
/** 业务ID */
@Column(name = "c_id")
private String cId;
/**
* 业务名称
*/
/** 业务名称 */
@Column(name = "c_name")
private String cName;
/**
* 执行状态
*/
/** 执行状态 */
@Column(name = "c_status")
private Byte cStatus;
/**
* 1-场景联动,2-数据流转
*/
/** 1-场景联动,2-数据流转 */
@Column(name = "c_type")
private Byte cType;
/**
* 条件
*/
/** 条件 */
private String conditions;
@Column(name = "create_time")
@@ -29,60 +29,41 @@ import lombok.NoArgsConstructor;
@Builder
public class IoTDeviceShadow implements Serializable {
@Id
private Long id;
@Id private Long id;
/**
* 本平台设备唯一标识符
*/
/** 本平台设备唯一标识符 */
@Column(name = "iot_id")
private String iotId;
/**
* 产品KEY
*/
/** 产品KEY */
@Column(name = "product_key")
private String productKey;
/**
* 设备自身序号
*/
/** 设备自身序号 */
@Column(name = "device_id")
private String deviceId;
/**
* 第三方平台设备ID唯一标识符
*/
/** 第三方平台设备ID唯一标识符 */
@Column(name = "ext_device_id")
private String extDeviceId;
/**
* 注册时间
*/
/** 注册时间 */
@Column(name = "active_time")
private Date activeTime;
/**
* 激活时间
*/
/** 激活时间 */
@Column(name = "online_time")
private Date onlineTime;
/**
* 最后通信时间
*/
/** 最后通信时间 */
@Column(name = "last_time")
private Date lastTime;
/**
* 更新时间
*/
/** 更新时间 */
@Column(name = "update_date")
private Date updateDate;
/**
* 影子数据
*/
/** 影子数据 */
private String metadata;
@Column(name = "`instance`")
@@ -30,70 +30,47 @@ import lombok.NoArgsConstructor;
@Builder
public class IoTDeviceSubscribe implements Serializable {
@Id
private Long id;
@Id private Long id;
/**
* 消息类别:属性(PROPERTIES),指令(REPLY),事件(EVENT),上下线(EVENT:online,offline),所有
*/
/** 消息类别:属性(PROPERTIES),指令(REPLY),事件(EVENT),上下线(EVENT:online,offline),所有 */
@Column(name = "msg_type")
private String msgType;
/**
* 订阅级别:设备级,产品级
*/
/** 订阅级别:设备级,产品级 */
@Column(name = "sub_type")
private String subType;
/**
* 设备deviceId
*/
/** 设备deviceId */
@Column(name = "device_id")
private String deviceId;
/**
* 产品ID或者设备唯一标识
*/
/** 产品ID或者设备唯一标识 */
@Column(name = "product_key")
private String productKey;
/**
* 产品ID或者设备唯一标识
*/
/** 产品ID或者设备唯一标识 */
@Column(name = "iot_id")
private String iotId;
/**
* 订阅地址
*/
/** 订阅地址 */
private String url;
/**
* 主题
*/
/** 主题 */
private String topic;
/**
* 创建时间
*/
/** 创建时间 */
@Column(name = "create_date")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createDate;
/**
* 创建人
*/
/** 创建人 */
private String creater;
/**
* 实例编号(应用标识)
*/
/** 实例编号(应用标识) */
@Column(name = "`instance`")
private String instance;
/**
* 是否启用
*/
/** 是否启用 */
private Boolean enabled;
private static final long serialVersionUID = 1L;
@@ -28,18 +28,13 @@ import lombok.NoArgsConstructor;
@Builder
public class IoTDeviceTags implements Serializable {
@Id
private Long id;
@Id private Long id;
/**
* 设备deviceId
*/
/** 设备deviceId */
@Column(name = "device_id")
private String deviceId;
/**
* 产品ID或者设备唯一标识
*/
/** 产品ID或者设备唯一标识 */
@Column(name = "product_key")
private String productKey;
@@ -12,6 +12,7 @@
package cn.universal.persistence.entity;
import cn.universal.persistence.annotation.SensitiveField;
import jakarta.persistence.Column;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@@ -31,155 +32,103 @@ public class IoTProduct implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@Id
private Long id;
/** 主键ID */
@Id private Long id;
/**
* 产品编号
*/
/** 产品编号 */
@Column(name = "product_id")
private String productId;
/**
* 产品标签
*/
/** 产品标签 */
@Column(name = "tags")
private String tags;
/**
* 产品KEY
*/
/** 产品KEY */
@Column(name = "product_key")
private String productKey;
/**
* 产品密钥(1型1密)
*/
/** 产品密钥(1型1密) */
@Column(name = "product_secret")
@SensitiveField(showPrefix = 3, showSuffix = 3)
private String productSecret;
/**
* 第三方平台:lvzhou,ctwing,onenet,alibaba,baidu
*/
/** 第三方平台:lvzhou,ctwing,onenet,alibaba,baidu */
@Column(name = "third_platform")
private String thirdPlatform;
/**
* 第三方平台配置信息
*/
/** 第三方平台配置信息 */
@Column(name = "third_configuration")
private String thirdConfiguration;
/**
* 厂商编号
*/
/** 厂商编号 */
@Column(name = "company_no")
private String companyNo;
/**
* 分类ID
*/
/** 分类ID */
@Column(name = "classified_id")
private String classifiedId;
/**
* 网络组件:关联network表
*/
/** 网络组件:关联network表 */
@Column(name = "network_union_id")
private String networkUnionId;
/**
* 设备类型: 网关,设备
*/
/** 设备类型: 网关,设备 */
@Column(name = "device_node")
private String deviceNode;
/**
* 所属项目
*/
/** 所属项目 */
@Column(name = "gw_product_key")
private String gwProductKey;
/**
* 分类名称
*/
/** 分类名称 */
@Column(name = "classified_name")
private String classifiedName;
/**
* 消息协议:
*/
/** 消息协议: */
@Column(name = "message_protocol")
private String messageProtocol;
/**
* 名称
*/
/** 名称 */
private String name;
/**
* 创建者id
*/
/** 创建者id */
@Column(name = "creator_id")
private String creatorId;
/**
* 产品状态
*/
/** 产品状态 */
private Byte state;
/**
* 说明
*/
/** 说明 */
@Column(name = "`describe`")
private String describe;
/**
* 数据存储策略
*/
/** 数据存储策略 */
@Column(name = "store_policy")
private String storePolicy;
/**
* 传输协议: MQTT,COAP,UDP
*/
/** 传输协议: MQTT,COAP,UDP */
@Column(name = "transport_protocol")
private String transportProtocol;
/**
* 图片地址
*/
/** 图片地址 */
@Column(name = "photo_url")
private String photoUrl;
/**
* 创建时间
*/
/** 创建时间 */
@Column(name = "create_time")
private Long createTime;
/**
* 协议配置
*/
/** 协议配置 */
private String configuration;
/**
* 数据存储策略配置
*/
/** 数据存储策略配置 */
@Column(name = "store_policy_configuration")
private String storePolicyConfiguration;
/**
* 物模型
*/
/** 物模型 */
private String metadata;
/**
* 第三方平台产品下发信息
*/
/** 第三方平台产品下发信息 */
private String thirdDownRequest;
@Column(name = "update_time")
@@ -28,57 +28,39 @@ public class IoTProductSort implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
/** id */
private String id;
/**
* 父id
*/
/** 父id */
@Excel(name = "父id")
private String parentId;
/**
* 是否有子节点
*/
/** 是否有子节点 */
@Excel(name = "是否有子节点")
private Integer hasChild;
/**
* 标识
*/
/** 标识 */
@Excel(name = "标识")
private String identification;
/**
* 分类名称
*/
/** 分类名称 */
@Excel(name = "分类名称")
private String classifiedName;
/**
* 说明
*/
/** 说明 */
@Excel(name = "说明")
private String description;
/**
* 创建时间
*/
/** 创建时间 */
@Excel(name = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 创建者
*/
/** 创建者 */
@Excel(name = "创建者")
private String createBy;
/**
* 子分类
*/
/** 子分类 */
private List<IoTProductSort> children = new ArrayList<IoTProductSort>();
public List<IoTProductSort> getChildren() {
@@ -40,109 +40,76 @@ import lombok.NoArgsConstructor;
public class IoTUser implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long id;
@Id private Long id;
/**
* 归属组织机构
*/
/** 归属组织机构 */
@Column(name = "org_id")
@Excel(name = "归属组织机构")
private Long orgId;
/**
* 用户名
*/
/** 用户名 */
@Excel(name = "用户名")
private String username;
/**
* 密码
*/
/** 密码 */
@JsonIgnore
@Excel(name = "密码")
private String password;
/**
* 别名
*/
/** 别名 */
@Excel(name = "别名")
private String alias;
/**
* 用户唯一标识
*/
/** 用户唯一标识 */
@Excel(name = "用户唯一标识")
@Column(name = "union_id")
private String unionId;
/**
* 邮箱
*/
/** 邮箱 */
@Excel(name = "邮箱")
private String email;
/**
* 密码加盐
*/
/** 密码加盐 */
@Excel(name = "密码加盐")
private String salt;
/**
* 手机号
*/
/** 手机号 */
@Excel(name = "手机号")
private String mobile;
/**
* 账号状态(0正常,1停用)
*/
/** 账号状态(0正常,1停用) */
@Excel(name = "账号状态(0正常,1停用")
private String status;
/**
* 头像
*/
/** 头像 */
@Excel(name = "头像")
private String avatar;
/**
* 上级用户唯一id
*/
/** 上级用户唯一id */
@Excel(name = "上级用户唯一id")
private String parentUnionId;
/**
* 账号身份 0.超级管理员 1.普通用户 2.子用户
*/
/** 账号身份 0.超级管理员 1.普通用户 2.子用户 */
@Excel(name = "账号身份 0.超级管理员 1.普通用户 2.子用户")
private Integer identity;
/**
* 登录IP
*/
/** 登录IP */
@Column(name = "login_ip")
@Excel(name = "登录IP")
private String loginIp;
/**
* 登录时间
*/
/** 登录时间 */
@Column(name = "login_date")
@Excel(name = "登录时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date loginDate;
/**
* 创建者
*/
/** 创建者 */
@Column(name = "create_by")
@Excel(name = "创建者")
private String createBy;
/**
* 创建时间
*/
/** 创建时间 */
@Column(name = "create_date")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "创建时间")
@@ -157,22 +124,16 @@ public class IoTUser implements Serializable {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateDate;
/**
* 备注
*/
/** 备注 */
@Excel(name = "备注")
private String remark;
/**
* 接入数
*/
/** 接入数 */
@Column(name = "license")
@Excel(name = "接入数")
private Integer license;
/**
* 接入数
*/
/** 接入数 */
@Column(name = "license_total_amount")
@Excel(name = "接入数总额度")
private Integer licenseTotalAmount;
@@ -183,16 +144,11 @@ public class IoTUser implements Serializable {
@Excel(name = "注册来源")
private String registerFrom;
/**
* 0-正常,1-删除
*/
/** 0-正常,1-删除 */
private Integer deleted;
/**
* 请求参数
*/
@Builder.Default
private Map<String, Object> params = new HashMap<>();
/** 请求参数 */
@Builder.Default private Map<String, Object> params = new HashMap<>();
public IoTUser(Long userId) {
this.id = userId;
@@ -40,57 +40,41 @@ public class IoTUserApplication implements Serializable {
@Excel(name = "用户唯一标识")
private String unionId;
/**
* 调用凭证APPID
*/
/** 调用凭证APPID */
@Column(name = "app_id")
@Excel(name = "调用凭证APPID")
private String appId;
/**
* MQ上行主题,英文逗号分隔
*/
/** MQ上行主题,英文逗号分隔 */
@Column(name = "up_topic")
@Excel(name = "MQ上行主题")
private String upTopic;
/**
* MQ下行主题,英文逗号分隔
*/
/** MQ下行主题,英文逗号分隔 */
@Column(name = "down_topic")
@Excel(name = "MQ下行主题")
private String downTopic;
/**
* 调用密钥
*/
/** 调用密钥 */
@Column(name = "app_secret")
@Excel(name = "调用密钥")
private String appSecret;
/**
* 授权结束时间
*/
/** 授权结束时间 */
@Column(name = "valid_end_date")
@Excel(name = "授权结束时间")
private Date validEndDate;
/**
* 授权范围
*/
/** 授权范围 */
@Excel(name = "授权范围")
private String scope;
/**
* 0-正常,1-停用
*/
/** 0-正常,1-停用 */
@Column(name = "app_status")
@Excel(name = "应用状态 0-正常,1-停用")
private Integer appStatus;
/**
* 0-正常,1-删除
*/
/** 0-正常,1-删除 */
@Excel(name = "是否删除 0正常 1删除")
private Integer deleted;
@@ -29,17 +29,12 @@ import lombok.NoArgsConstructor;
@Builder
public class Network implements Serializable {
@Id
private Integer id;
@Id private Integer id;
/**
* TCP_CLIENT,MQTT_CLIENT,HTTP_CLIENT,WEB_SOCKET_CLIENT
*/
/** TCP_CLIENT,MQTT_CLIENT,HTTP_CLIENT,WEB_SOCKET_CLIENT */
private String type;
/**
* 唯一标识
*/
/** 唯一标识 */
@Column(name = "product_key")
private String productKey;
@@ -48,27 +43,19 @@ public class Network implements Serializable {
private String name;
/**
* 详细描述
*/
/** 详细描述 */
private String description;
@Column(name = "create_date")
private Date createDate;
/**
* enable,disable
*/
/** enable,disable */
private Boolean state;
/**
* 配置内容
*/
/** 配置内容 */
private String configuration;
/**
* 创建用户
*/
/** 创建用户 */
@Column(name = "create_user")
private String createUser;
@@ -32,22 +32,16 @@ import lombok.experimental.Accessors;
@Accessors(chain = true)
public class OAuth2ClientDetails implements Serializable {
/**
* 客户端标识
*/
/** 客户端标识 */
@Id
@Column(name = "client_id")
private String clientId;
/**
* 资源标识
*/
/** 资源标识 */
@Column(name = "resource_ids")
private String resourceIds;
/**
* 客户端秘钥
*/
/** 客户端秘钥 */
@Column(name = "client_secret")
private String clientSecret;
@@ -60,50 +54,34 @@ public class OAuth2ClientDetails implements Serializable {
@Column(name = "Authorized_grant_types")
private String AuthorizedGrantTypes;
/**
* 授权成功后回调URL
*/
/** 授权成功后回调URL */
@Column(name = "web_server_redirect_uri")
private String webServerRedirectUri;
private String Authorities;
/**
* 访问令牌的有效时间
*/
/** 访问令牌的有效时间 */
@Column(name = "access_token_validity")
private Integer accessTokenValidity;
/**
* 刷新令牌的有效时间
*/
/** 刷新令牌的有效时间 */
@Column(name = "refresh_token_validity")
private Integer refreshTokenValidity;
/**
* 额外的信息,备用
*/
/** 额外的信息,备用 */
@Column(name = "additional_information")
private String additionalInformation;
/**
* 自动批准标识:0代表不显示授权页面 1显示授权页面
*/
/** 自动批准标识:0代表不显示授权页面 1显示授权页面 */
private String autoapprove;
/**
* 用户名
*/
/** 用户名 */
private String username;
/**
* 密码
*/
/** 密码 */
private String password;
/**
* 唯一标识
*/
/** 唯一标识 */
@Column(name = "iot_union_id")
private String iotUnionId;
@@ -182,7 +160,7 @@ public class OAuth2ClientDetails implements Serializable {
* client_credentials:客户端模式
*
* @return Authorized_grant_types - 授权类型 Authorization_code:授权码模式 refresh_token:刷新token
* password:密码模式 implicit:隐私授权模式 client_credentials:客户端模式
* password:密码模式 implicit:隐私授权模式 client_credentials:客户端模式
*/
public String getAuthorizedGrantTypes() {
return AuthorizedGrantTypes;
@@ -193,7 +171,7 @@ public class OAuth2ClientDetails implements Serializable {
* client_credentials:客户端模式
*
* @param AuthorizedGrantTypes 授权类型 Authorization_code:授权码模式 refresh_token:刷新token password:密码模式
* implicit:隐私授权模式 client_credentials:客户端模式
* implicit:隐私授权模式 client_credentials:客户端模式
*/
public void setAuthorizedGrantTypes(String AuthorizedGrantTypes) {
this.AuthorizedGrantTypes = AuthorizedGrantTypes;
@@ -39,75 +39,55 @@ public class RuleModel implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
/** 主键ID */
@Id
@KeySql(genId = SQenGenId.class)
@Schema(description = "主键ID")
private Long id;
/**
* 规则名称
*/
/** 规则名称 */
@Schema(description = "规则名称")
@Column(name = "rule_name")
private String ruleName;
/**
* 数据级别
*/
/** 数据级别 */
@Schema(description = "数据级别")
@Column(name = "data_level")
private String dataLevel;
/**
* 描述
*/
/** 描述 */
@Schema(description = "描述")
@Column(name = "description")
private String description;
/**
* 状态
*/
/** 状态 */
@Schema(description = "状态")
@Column(name = "status")
private String status;
;
/**
* 产品KEY
*/
/** 产品KEY */
@Schema(description = "产品KEY")
@Column(name = "product_key")
private String productKey;
/**
* 规则配置
*/
/** 规则配置 */
@Schema(description = "规则配置")
@Column(name = "config")
private String config;
/**
* 创建人
*/
/** 创建人 */
@Schema(description = "创建人")
@Column(name = "creator_id")
private String creatorId;
/**
* 创建时间
*/
/** 创建时间 */
@Schema(description = "创建时间")
@Column(name = "create_time")
private Date createTime;
/**
* 创建时间
*/
/** 创建时间 */
@Schema(description = "创建时间")
@Column(name = "update_time")
private Date updateTime;
@@ -37,28 +37,20 @@ public class RuleModelInstance implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
/** 主键ID */
@Id
@KeySql(genId = SQenGenId.class)
private Long id;
/**
* 模型id
*/
/** 模型id */
@Column(name = "model_id")
private Long modelId;
/**
* 关联类型
*/
/** 关联类型 */
@Column(name = "relation_type")
private String relationType;
/**
* 关联id
*/
/** 关联id */
@Column(name = "relation_id")
private String relationId;
}
@@ -40,87 +40,63 @@ public class RulegoChain implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
/** 主键ID */
@Id
@KeySql(genId = SQenGenId.class)
@Schema(description = "主键ID")
private Long id;
/**
* rulego规则链ID
*/
/** rulego规则链ID */
@Schema(description = "rulego规则链ID")
@Column(name = "rulego_id")
private String rulegoId;
/**
* 规则链名称
*/
/** 规则链名称 */
@Schema(description = "规则链名称")
@Column(name = "chain_name")
private String chainName;
/**
* 规则链描述
*/
/** 规则链描述 */
@Schema(description = "规则链描述")
@Column(name = "description")
private String description;
/**
* 创建人unionId
*/
/** 创建人unionId */
@Schema(description = "创建人unionId")
@Column(name = "creator_id")
private String creatorId;
/**
* 创建人姓名
*/
/** 创建人姓名 */
@Schema(description = "创建人姓名")
@Column(name = "creator_name")
private String creatorName;
/**
* 状态:draft-草稿,deployed-已部署,stopped-已停止
*/
/** 状态:draft-草稿,deployed-已部署,stopped-已停止 */
@Schema(description = "状态")
@Column(name = "status")
private String status;
/**
* 规则链DSL内容(JSON格式)
*/
/** 规则链DSL内容(JSON格式) */
@Schema(description = "规则链DSL内容")
@Column(name = "dsl_content")
private String dslContent;
/**
* 最后同步时间
*/
/** 最后同步时间 */
@Schema(description = "最后同步时间")
@Column(name = "last_sync_time")
private Date lastSyncTime;
/**
* 创建时间
*/
/** 创建时间 */
@Schema(description = "创建时间")
@Column(name = "create_time")
private Date createTime;
/**
* 更新时间
*/
/** 更新时间 */
@Schema(description = "更新时间")
@Column(name = "update_time")
private Date updateTime;
/**
* 是否删除:0-未删除,1-已删除
*/
/** 是否删除:0-未删除,1-已删除 */
@Schema(description = "是否删除")
@Column(name = "deleted")
private Integer deleted;
@@ -40,73 +40,53 @@ public class RulegoChainLog implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
/** 主键ID */
@Id
@KeySql(genId = SQenGenId.class)
@Schema(description = "主键ID")
private Long id;
/**
* rulego规则链ID
*/
/** rulego规则链ID */
@Schema(description = "rulego规则链ID")
@Column(name = "rulego_id")
private String rulegoId;
/**
* 规则链名称
*/
/** 规则链名称 */
@Schema(description = "规则链名称")
@Column(name = "chain_name")
private String chainName;
/**
* 执行ID
*/
/** 执行ID */
@Schema(description = "执行ID")
@Column(name = "execution_id")
private String executionId;
/**
* 输入数据
*/
/** 输入数据 */
@Schema(description = "输入数据")
@Column(name = "input_data")
private String inputData;
/**
* 输出数据
*/
/** 输出数据 */
@Schema(description = "输出数据")
@Column(name = "output_data")
private String outputData;
/**
* 执行状态:success-成功,failed-失败
*/
/** 执行状态:success-成功,failed-失败 */
@Schema(description = "执行状态")
@Column(name = "execution_status")
private String executionStatus;
/**
* 错误信息
*/
/** 错误信息 */
@Schema(description = "错误信息")
@Column(name = "error_message")
private String errorMessage;
/**
* 执行耗时(毫秒)
*/
/** 执行耗时(毫秒) */
@Schema(description = "执行耗时")
@Column(name = "execution_time")
private Long executionTime;
/**
* 创建时间
*/
/** 创建时间 */
@Schema(description = "创建时间")
@Column(name = "create_time")
private Date createTime;
@@ -36,54 +36,39 @@ public class SceneLinkage extends BaseEntity {
private static final long serialVersionUID = 1L;
@Id
private Long id;
@Id private Long id;
/**
* 场景名称
*/
/** 场景名称 */
@Excel(name = "场景名称")
@Column(name = "scene_name")
private String sceneName;
/**
* 触发条件 all.全部 one.任意一个
*/
/** 触发条件 all.全部 one.任意一个 */
@Excel(name = "触发条件 all.全部 one.任意一个")
@Column(name = "touch")
private String touch;
/**
* 触发条件
*/
/** 触发条件 */
@Excel(name = "触发条件")
@Column(name = "trigger_condition")
private String triggerCondition;
/**
* 执行动作
*/
/** 执行动作 */
@Excel(name = "执行动作")
@Column(name = "exec_action")
private String execAction;
/**
* 沉默周期
*/
/** 沉默周期 */
@Excel(name = "沉默周期")
@Column(name = "sleep_cycle")
private Integer sleepCycle;
/**
* 0启用 1停用
*/
/** 0启用 1停用 */
@Excel(name = "0启用 1停用")
@Column(name = "status")
private Integer status;
/**
* 设备id
*/
/** 设备id */
@Excel(name = "设备id")
@Column(name = "dev_id")
private String devId;
@@ -28,36 +28,23 @@ import lombok.NoArgsConstructor;
public class SupportMapAreas implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private String id;
@Id private String id;
/**
* 父id
*/
/** 父id */
private String pid;
/**
* 地址
*/
/** 地址 */
private String name;
/**
* 详细地址
*/
/** 详细地址 */
private String fullName;
/**
* 深度
*/
/** 深度 */
private String deep;
/**
* 核心点坐标
*/
/** 核心点坐标 */
private String location;
/**
* 范围坐标
*/
/** 范围坐标 */
private String polygon;
}
@@ -25,9 +25,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 系统访问记录表 sys_logininfor @Author ruoyi
*/
/** 系统访问记录表 sys_logininfor @Author ruoyi */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@@ -35,65 +33,44 @@ public class SysLogininfor implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
/** ID */
@Excel(name = "序号", cellType = ColumnType.NUMERIC)
@Id
private Long infoId;
/**
* 用户账号
*/
/** 用户账号 */
@Excel(name = "用户账号")
private String userName;
/**
* 登录状态 0成功 1失败
*/
/** 登录状态 0成功 1失败 */
@Excel(name = "登录状态", readConverterExp = "0=成功,1=失败")
private String status;
/**
* 登录IP地址
*/
/** 登录IP地址 */
@Excel(name = "登录地址")
private String ipaddr;
/**
* 登录地点
*/
/** 登录地点 */
@Excel(name = "登录地点")
private String loginLocation;
/**
* 浏览器类型
*/
/** 浏览器类型 */
@Excel(name = "浏览器")
private String browser;
/**
* 操作系统
*/
/** 操作系统 */
@Excel(name = "操作系统")
private String os;
/**
* 提示消息
*/
/** 提示消息 */
@Excel(name = "提示消息")
private String msg;
/**
* 访问时间
*/
/** 访问时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date loginTime;
/**
* 请求参数
*/
@Transient
private Map<String, Object> params = new HashMap<>();
/** 请求参数 */
@Transient private Map<String, Object> params = new HashMap<>();
}
@@ -25,9 +25,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 操作日志记录表 oper_log @Author ruoyi
*/
/** 操作日志记录表 oper_log @Author ruoyi */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@@ -35,113 +33,75 @@ public class SysOperLog implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 日志主键
*/
/** 日志主键 */
@Excel(name = "操作序号", cellType = ColumnType.NUMERIC)
@Id
private Long operId;
/**
* 操作模块
*/
/** 操作模块 */
@Excel(name = "操作模块")
private String title;
/**
* 业务类型(0其它 1新增 2修改 3删除)
*/
/** 业务类型(0其它 1新增 2修改 3删除) */
@Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据")
private Integer businessType;
/**
* 业务类型数组
*/
@Transient
private Integer[] businessTypes;
/** 业务类型数组 */
@Transient private Integer[] businessTypes;
/**
* 请求方法
*/
/** 请求方法 */
@Excel(name = "请求方法")
private String method;
/**
* 请求方式
*/
/** 请求方式 */
@Excel(name = "请求方式")
private String requestMethod;
/**
* 操作类别(0其它 1后台用户 2手机端用户)
*/
/** 操作类别(0其它 1后台用户 2手机端用户) */
@Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户")
private Integer operatorType;
/**
* 操作人员
*/
/** 操作人员 */
@Excel(name = "操作人员")
private String operName;
/**
* 部门名称
*/
/** 部门名称 */
@Excel(name = "部门名称")
private String deptName;
/**
* 请求url
*/
/** 请求url */
@Excel(name = "请求地址")
private String operUrl;
/**
* 操作地址
*/
/** 操作地址 */
@Excel(name = "操作地址")
private String operIp;
/**
* 操作地点
*/
/** 操作地点 */
@Excel(name = "操作地点")
private String operLocation;
/**
* 请求参数
*/
/** 请求参数 */
@Excel(name = "请求参数")
private String operParam;
/**
* 返回参数
*/
/** 返回参数 */
@Excel(name = "返回参数")
private String jsonResult;
/**
* 操作状态(0正常 1异常)
*/
/** 操作状态(0正常 1异常) */
@Excel(name = "状态", readConverterExp = "0=正常,1=异常")
private Integer status;
/**
* 错误消息
*/
/** 错误消息 */
@Excel(name = "错误消息")
private String errorMsg;
/**
* 操作时间
*/
/** 操作时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date operTime;
/**
* 请求参数
*/
@Transient
private Map<String, Object> params = new HashMap<>();
/** 请求参数 */
@Transient private Map<String, Object> params = new HashMap<>();
}
@@ -25,9 +25,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 参数配置表 sys_config @Author ruoyi
*/
/** 参数配置表 sys_config @Author ruoyi */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@@ -38,70 +36,47 @@ public class SysConfig {
private static final long serialVersionUID = 1L;
/**
* 参数主键
*/
@Id
private Long configId;
/** 参数主键 */
@Id private Long configId;
/**
* 参数名称
*/
/** 参数名称 */
@Column(name = "config_name")
private String configName;
/**
* 参数键名
*/
/** 参数键名 */
@Column(name = "config_key")
private String configKey;
/**
* 参数键值
*/
/** 参数键值 */
@Column(name = "config_value")
private String configValue;
/**
* 系统内置(Y是 N否)
*/
/** 系统内置(Y是 N否) */
@Column(name = "config_type")
private String configType;
/**
* 创建者
*/
/** 创建者 */
@Column(name = "create_by")
private String createBy;
/**
* 创建时间
*/
/** 创建时间 */
@Column(name = "create_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新者
*/
/** 更新者 */
@Column(name = "update_by")
private String updateBy;
/**
* 更新时间
*/
/** 更新时间 */
@Column(name = "update_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/**
* 备注
*/
/** 备注 */
private String remark;
/**
* 请求参数
*/
/** 请求参数 */
// @TableField(exist = false)
private Map<String, Object> params = new HashMap<>();
}
@@ -29,9 +29,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 字典数据表 sys_dict_data @Author ruoyi
*/
/** 字典数据表 sys_dict_data @Author ruoyi */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@@ -42,111 +40,80 @@ public class SysDictData implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 字典编码
*/
/** 字典编码 */
// @Excel(name = "字典编码", cellType = Excel.ColumnType.NUMERIC)
@Id
private Long dictCode;
@Id private Long dictCode;
/**
* 字典排序
*/
/** 字典排序 */
// @Excel(name = "字典排序", cellType = Excel.ColumnType.NUMERIC)
@Column(name = "dict_sort")
private Long dictSort;
/**
* 字典标签
*/
/** 字典标签 */
// @Excel(name = "字典标签")
@NotBlank(message = "字典标签不能为空")
@Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符")
@Column(name = "dict_label")
private String dictLabel;
/**
* 字典键值
*/
/** 字典键值 */
// @Excel(name = "字典键值")
@NotBlank(message = "字典键值不能为空")
@Column(name = "dict_value")
@Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符")
private String dictValue;
/**
* 字典类型
*/
/** 字典类型 */
// @Excel(name = "字典类型")
@NotBlank(message = "字典类型不能为空")
@Column(name = "dict_type")
@Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符")
private String dictType;
/**
* 样式属性(其他样式扩展)
*/
/** 样式属性(其他样式扩展) */
@Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符")
@Column(name = "css_class")
private String cssClass;
/**
* 表格字典样式
*/
/** 表格字典样式 */
@Column(name = "list_class")
private String listClass;
/**
* 是否默认(Y是 N否)
*/
/** 是否默认(Y是 N否) */
// @Excel(name = "是否默认", readConverterExp = "Y=是,N=否")
@Column(name = "is_default")
private String isDefault;
/**
* 状态(0正常 1停用)
*/
/** 状态(0正常 1停用) */
// @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
private String status;
/**
* 创建者
*/
/** 创建者 */
// @TableField(fill = FieldFill.INSERT)
@Column(name = "create_by")
private String createBy;
/**
* 创建时间
*/
/** 创建时间 */
// @TableField(fill = FieldFill.INSERT)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "create_time")
private Date createTime;
/**
* 更新者
*/
/** 更新者 */
// @TableField(fill = FieldFill.INSERT_UPDATE)
@Column(name = "update_by")
private String updateBy;
/**
* 更新时间
*/
/** 更新时间 */
// @TableField(fill = FieldFill.INSERT_UPDATE)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "update_time")
private Date updateTime;
/**
* 备注
*/
/** 备注 */
private String remark;
/**
* 请求参数
*/
/** 请求参数 */
// @TableField(exist = false)
private Map<String, Object> params = new HashMap<>();
@@ -26,9 +26,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 字典类型表 sys_dict_type @Author ruoyi
*/
/** 字典类型表 sys_dict_type @Author ruoyi */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@@ -37,76 +35,55 @@ public class SysDictType implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 字典主键
*/
/** 字典主键 */
// @Excel(name = "字典主键", cellType = Excel.ColumnType.NUMERIC)
// @TableId(value = "dict_id", type = IdType.AUTO)
@Id
private Long dictId;
@Id private Long dictId;
/**
* 字典名称
*/
/** 字典名称 */
// @Excel(name = "字典名称")
@NotBlank(message = "字典名称不能为空")
@Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符")
@Column(name = "dict_name")
private String dictName;
/**
* 字典类型
*/
/** 字典类型 */
// @Excel(name = "字典类型")
@NotBlank(message = "字典类型不能为空")
@Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符")
@Column(name = "dict_type")
private String dictType;
/**
* 状态(0正常 1停用)
*/
/** 状态(0正常 1停用) */
// @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
private String status;
/**
* 创建者
*/
/** 创建者 */
// @TableField(fill = FieldFill.INSERT)
@Column(name = "create_by")
private String createBy;
/**
* 创建时间
*/
/** 创建时间 */
// @TableField(fill = FieldFill.INSERT)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "create_time")
private Date createTime;
/**
* 更新者
*/
/** 更新者 */
// @TableField(fill = FieldFill.INSERT_UPDATE)
@Column(name = "update_by")
private String updateBy;
/**
* 更新时间
*/
/** 更新时间 */
// @TableField(fill = FieldFill.INSERT_UPDATE)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "update_time")
private Date updateTime;
/**
* 备注
*/
/** 备注 */
private String remark;
/**
* 请求参数
*/
/** 请求参数 */
// @TableField(exist = false)
private Map<String, Object> params = new HashMap<>();
}
@@ -28,9 +28,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 菜单权限表 sys_menu @Author ruoyi
*/
/** 菜单权限表 sys_menu @Author ruoyi */
@Data
@NoArgsConstructor
@AllArgsConstructor
@@ -41,15 +39,10 @@ public class SysMenu implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 菜单ID
*/
@Id
private Long menuId;
/** 菜单ID */
@Id private Long menuId;
/**
* 菜单名称
*/
/** 菜单名称 */
@Column(name = "menu_name")
private String menuName;
@@ -57,106 +50,68 @@ public class SysMenu implements Serializable {
// @Column(name = "parent_name")
// private String parentName;
/**
* 父菜单ID
*/
/** 父菜单ID */
@Column(name = "parent_id")
private Long parentId;
/**
* 显示顺序
*/
/** 显示顺序 */
@Column(name = "order_num")
private String orderNum;
/**
* 路由地址
*/
/** 路由地址 */
private String path;
/**
* 组件路径
*/
/** 组件路径 */
private String component;
/**
* 是否为外链(0是 1否)
*/
/** 是否为外链(0是 1否) */
@Column(name = "is_frame")
private String isFrame;
/**
* 是否缓存(0缓存 1不缓存)
*/
/** 是否缓存(0缓存 1不缓存) */
@Column(name = "is_cache")
private String isCache;
/**
* 类型(M目录 C菜单 F按钮)
*/
/** 类型(M目录 C菜单 F按钮) */
@Column(name = "menu_type")
private String menuType;
/**
* 显示状态(0显示 1隐藏)
*/
/** 显示状态(0显示 1隐藏) */
private String visible;
/**
* 菜单状态(0显示 1隐藏)
*/
/** 菜单状态(0显示 1隐藏) */
private String status;
/**
* 权限字符串
*/
/** 权限字符串 */
private String perms;
/**
* 菜单图标
*/
/** 菜单图标 */
private String icon;
/**
* 创建者
*/
/** 创建者 */
@Column(name = "create_by")
private String createBy;
/**
* 创建时间
*/
/** 创建时间 */
@Column(name = "create_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新者
*/
/** 更新者 */
@Column(name = "update_by")
private String updateBy;
/**
* 更新时间
*/
/** 更新时间 */
@Column(name = "update_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/**
* 备注
*/
/** 备注 */
private String remark;
/**
* 请求参数
*/
@Builder.Default
private Map<String, Object> params = new HashMap<>();
/** 请求参数 */
@Builder.Default private Map<String, Object> params = new HashMap<>();
/**
* 子菜单
*/
@Builder.Default
private List<SysMenu> children = new ArrayList<SysMenu>();
/** 子菜单 */
@Builder.Default private List<SysMenu> children = new ArrayList<SysMenu>();
}
@@ -27,9 +27,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 角色表 sys_role @Author ruoyi
*/
/** 角色表 sys_role @Author ruoyi */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@@ -40,104 +38,68 @@ public class SysRole implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 角色ID
*/
@Id
private Long roleId;
/** 角色ID */
@Id private Long roleId;
/**
* 角色名称
*/
/** 角色名称 */
@Excel(name = "角色名称")
@Column(name = "role_name")
private String roleName;
/**
* 角色权限
*/
/** 角色权限 */
@Column(name = "role_key")
private String roleKey;
/**
* 角色排序
*/
/** 角色排序 */
@Column(name = "role_sort")
private String roleSort;
/**
* 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限)
*/
/** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */
@Column(name = "data_scope")
private String dataScope;
/**
* 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示)
*/
/** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */
@Column(name = "menu_check_strictly")
private Boolean menuCheckStrictly;
/**
* 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 )
*/
/** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */
@Column(name = "dept_check_strictly")
private Boolean deptCheckStrictly;
/**
* 角色状态(0正常 1停用)
*/
/** 角色状态(0正常 1停用) */
private String status;
/**
* 创建者
*/
/** 创建者 */
@Column(name = "create_by")
private String createBy;
/**
* 创建时间
*/
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "create_time")
private Date createTime;
/**
* 更新者
*/
/** 更新者 */
@Column(name = "update_by")
private String updateBy;
/**
* 更新时间
*/
/** 更新时间 */
@Column(name = "update_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/**
* 备注
*/
/** 备注 */
private String remark;
/**
* 请求参数
*/
@Builder.Default
private Map<String, Object> params = new HashMap<>();
/** 请求参数 */
@Builder.Default private Map<String, Object> params = new HashMap<>();
/**
* 用户是否存在此角色标识 默认不存在
*/
/** 用户是否存在此角色标识 默认不存在 */
private boolean flag = false;
/**
* 菜单组
*/
/** 菜单组 */
private Long[] menuIds;
/**
* 部门组(数据权限)
*/
/** 部门组(数据权限) */
// private Long[] deptIds;
public SysRole(Long roleId) {
@@ -21,9 +21,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 角色和菜单关联 sys_role_menu @Author ruoyi
*/
/** 角色和菜单关联 sys_role_menu @Author ruoyi */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@@ -33,16 +31,11 @@ import lombok.experimental.Accessors;
public class SysRoleMenu implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long uuid;
@Id private Long uuid;
/**
* 角色ID
*/
/** 角色ID */
private Long roleId;
/**
* 菜单ID
*/
/** 菜单ID */
private Long menuId;
}
@@ -21,9 +21,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 用户和角色关联 sys_user_role @Author ruoyi
*/
/** 用户和角色关联 sys_user_role @Author ruoyi */
@Data
@NoArgsConstructor
@AllArgsConstructor
@@ -33,16 +31,11 @@ import lombok.experimental.Accessors;
public class SysUserRole implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long uuid;
@Id private Long uuid;
/**
* 用户ID
*/
/** 用户ID */
private String unionId;
/**
* 角色ID
*/
/** 角色ID */
private Long roleId;
}
@@ -20,9 +20,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* Treeselect树结构实体类 @Author ruoyi
*/
/** Treeselect树结构实体类 @Author ruoyi */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@@ -30,19 +28,13 @@ public class TreeSelect implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 节点ID
*/
/** 节点ID */
private Long id;
/**
* 节点名称
*/
/** 节点名称 */
private String label;
/**
* 子节点
*/
/** 子节点 */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<TreeSelect> children;
@@ -17,32 +17,22 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 路由显示信息 @Author ruoyi
*/
/** 路由显示信息 @Author ruoyi */
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class MetaVo {
/**
* 设置该路由在侧边栏和面包屑中展示的名字
*/
/** 设置该路由在侧边栏和面包屑中展示的名字 */
private String title;
/**
* 设置该路由的图标,对应路径src/assets/icons/svg
*/
/** 设置该路由的图标,对应路径src/assets/icons/svg */
private String icon;
/**
* 设置为true,则不会被 <keep-alive>缓存
*/
/** 设置为true,则不会被 <keep-alive>缓存 */
private boolean noCache;
/**
* 内链地址(http(s)://开头)
*/
/** 内链地址(http(s)://开头) */
private String link;
public MetaVo(String title, String icon) {
@@ -18,52 +18,34 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 路由配置信息 @Author ruoyi
*/
/** 路由配置信息 @Author ruoyi */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class RouterVo {
/**
* 路由名字
*/
/** 路由名字 */
private String name;
/**
* 路由地址
*/
/** 路由地址 */
private String path;
/**
* 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现
*/
/** 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 */
private boolean hidden;
/**
* 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
*/
/** 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 */
private String redirect;
/**
* 组件地址
*/
/** 组件地址 */
private String component;
/**
* 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
*/
/** 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 */
private Boolean alwaysShow;
/**
* 其他元素
*/
/** 其他元素 */
private MetaVo meta;
/**
* 子路由
*/
/** 子路由 */
private List<RouterVo> children;
}
@@ -21,9 +21,7 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 字典数据表 sys_dict_data @Author ruoyi
*/
/** 字典数据表 sys_dict_data @Author ruoyi */
@Data
@NoArgsConstructor
@AllArgsConstructor
@@ -31,89 +29,55 @@ public class SysDictDataVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 字典编码
*/
/** 字典编码 */
private Long dictCode;
/**
* 字典排序
*/
/** 字典排序 */
private Long dictSort;
/**
* 字典标签
*/
/** 字典标签 */
private String dictLabel;
/**
* 字典键值
*/
/** 字典键值 */
private String dictValue;
/**
* 字典类型
*/
/** 字典类型 */
private String dictType;
/**
* 样式属性(其他样式扩展)
*/
/** 样式属性(其他样式扩展) */
private String cssClass;
/**
* 表格字典样式
*/
/** 表格字典样式 */
private String listClass;
/**
* 是否默认(Y是 N否)
*/
/** 是否默认(Y是 N否) */
private String isDefault;
/**
* 状态(0正常 1停用)
*/
/** 状态(0正常 1停用) */
private String status;
/**
* 创建者
*/
/** 创建者 */
private String createBy;
/**
* 创建时间
*/
/** 创建时间 */
private Date createTime;
/**
* 更新者
*/
/** 更新者 */
private String updateBy;
/**
* 更新时间
*/
/** 更新时间 */
private Date updateTime;
/**
* 开始时间
*/
/** 开始时间 */
private String startTime;
/**
* 结束时间
*/
/** 结束时间 */
private String endTime;
/**
* 备注
*/
/** 备注 */
private String remark;
/**
* 请求参数
*/
/** 请求参数 */
// @TableField(exist = false)
private Map<String, Object> params = new HashMap<>();
@@ -19,9 +19,7 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 字典类型group分组 @Author ruoyi
*/
/** 字典类型group分组 @Author ruoyi */
@Data
@NoArgsConstructor
@AllArgsConstructor
@@ -30,116 +30,76 @@ public class IoTDeviceBO implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
/**
* 对外设备唯一标识符
*/
/** 对外设备唯一标识符 */
private String iotId;
/**
* Lora密钥
*/
/** Lora密钥 */
private String secretKey;
/**
* 归属应用
*/
/** 归属应用 */
private String application;
/**
* 激活时间
*/
/** 激活时间 */
private Integer registryTime;
/**
* 最后上线时间
*/
/** 最后上线时间 */
private Long onlineTime;
/**
* 设备自身序号
*/
/** 设备自身序号 */
private String deviceId;
/**
* 第三方设备ID唯一标识符
*/
/** 第三方设备ID唯一标识符 */
private String extDeviceId;
/**
* 别名
*/
/** 别名 */
private String nickName;
/**
* 设备名称
*/
/** 设备名称 */
private String productName;
private Long features;
/**
* 网关产品ProductKey
*/
/** 网关产品ProductKey */
private String gwProductKey;
/**
* 设备密钥
*/
private String gwDeviceId;
/** 设备密钥 */
private String deviceSecret;
/**
* 产品key
*/
/** 产品key */
private String productKey;
/**
* 设备实例名称
*/
/** 设备实例名称 */
private String deviceName;
private String creatorId;
private String creatorName;
/**
* 0-离线,1-在线
*/
/** 0-离线,1-在线 */
private Boolean state;
/**
* 说明
*/
/** 说明 */
private String detail;
private Long createTime;
/**
* 派生元数据,有的设备的属性,功能,事件可能会动态的添加
*/
/** 派生元数据,有的设备的属性,功能,事件可能会动态的添加 */
private String deriveMetadata;
/**
* 其他配置
*/
/** 其他配置 */
private String configuration;
/**
* 设备坐标
*/
/** 设备坐标 */
private String coordinate;
/**
* 区域ID
*/
/** 区域ID */
private String areasId;
/**
* 纬度
*/
/** 纬度 */
private String latitude;
/**
* 经度
*/
/** 经度 */
private String longitude;
private String fenceId;
@@ -148,24 +108,16 @@ public class IoTDeviceBO implements Serializable {
// private String parentUnionId;
/**
* 请求参数
*/
@Builder.Default
private Map<String, Object> params = new HashMap<>();
/** 请求参数 */
@Builder.Default private Map<String, Object> params = new HashMap<>();
private Boolean hasProtocol;
/**
* 接收额外参数
*/
@Builder.Default
private Map<String, Object> otherParams = new HashMap<>();
/** 接收额外参数 */
@Builder.Default private Map<String, Object> otherParams = new HashMap<>();
private List<String> devGroupName;
/**
* 所属父设备名称
*/
private String parentName;
/** 所属父设备信息 */
private String gwDeviceInfo;
}
@@ -48,9 +48,6 @@ public class IoTDeviceFunctionTaskBO {
private Boolean chooseAll;
private List<String> extParam;
/**
* 请求参数
*/
@Transient
private Map<String, Object> params = new HashMap<>();
/** 请求参数 */
@Transient private Map<String, Object> params = new HashMap<>();
}
@@ -17,9 +17,7 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*/
/** */
@Data
@AllArgsConstructor
@NoArgsConstructor
@@ -28,34 +26,22 @@ public class IoTDeviceGroupBO {
private static final long serialVersionUID = 1L;
/**
* 分组ID,非自增
*/
/** 分组ID,非自增 */
private Long id;
/**
* 设备id
*/
/** 设备id */
private String[] devIds;
/**
* 分组名称
*/
/** 分组名称 */
private String groupName;
/**
* 分组标识
*/
/** 分组标识 */
private String groupCode;
/**
* 群组描述
*/
/** 群组描述 */
private String groupDescribe;
/**
* 父id
*/
/** 父id */
private Long parentId;
// //账号父id
// private String parentUnionId;
@@ -29,84 +29,51 @@ public class IoTDeviceLogBO extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 日志ID,非自增
*/
/** 日志ID,非自增 */
private Long id;
/**
* 设备编码
*/
/** 设备编码 */
private String iotId;
/**
* 设备序列号
*/
/** 设备序列号 */
private String deviceId;
/**
* 第三方设备ID唯一标识符
*/
@Transient
private String extDeviceId;
/** 第三方设备ID唯一标识符 */
@Transient private String extDeviceId;
/**
* 产品ID
*/
/** 产品ID */
private String productKey;
/**
* 设备名称
*/
/** 设备名称 */
private String deviceName;
/**
* 消息类型
*/
/** 消息类型 */
private String messageType;
/**
* 公司名称
*/
/** 公司名称 */
private String companyNo;
/**
* 协议名称
*/
/** 协议名称 */
private String protocol;
/**
* 节点类型
*/
/** 节点类型 */
private String deviceNode;
/**
* 设备分类编号
*/
/** 设备分类编号 */
private String classifiedId;
/**
* 组织ID
*/
/** 组织ID */
private String orgId;
/**
* 事件名称
*/
/** 事件名称 */
private String event;
/**
* 创建人
*/
/** 创建人 */
private String createId;
/**
* 经纬度
*/
/** 经纬度 */
private String point;
/**
* 内容
*/
/** 内容 */
private String content;
}

Some files were not shown because too many files have changed in this diff Show More