refactor(core): 拆分 model/mqtt/update 为单一职责子模块

- model/ 按领域拆为 connection/subscription/message/publish/variable/log/settings
- mqtt/ 拆为门面(引擎与事件)、tls、v5、v4 四块
- update/ 拆为门面(版本工具与命名约定)、check(检查)、install(下载/安装/清理)
- sysmon 只保留进程资源采样,窗口置顶迁至新 platform.rs
- 对外路径经 mod.rs 再导出保持不变,测试随门面保留(33 通过)
This commit is contained in:
2026-09-29 22:20:53 +08:00
parent 303b348ffe
commit b5089f5898
21 changed files with 3393 additions and 3252 deletions
+71
View File
@@ -0,0 +1,71 @@
//! 全局变量:模板占位符渲染(含 {{timestamp}} 时间基一致性)。
use serde::{Deserialize, Serialize};
use super::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlobalVariable {
pub key: String,
pub value: String,
}
/// 解析 `{{key}}` 模板:
/// - 用户定义的全局变量优先;
/// - 内置变量:`{{$ts}}`(unix 秒)、`{{$ts_ms}}`(毫秒)、`{{$uuid}}`;
/// - 找不到的占位符保持原样。
pub fn render_template(input: &str, vars: &[GlobalVariable]) -> String {
let mut out = String::with_capacity(input.len());
let bytes = input.as_bytes();
// 同一次渲染内时间基准保持一致,避免一条消息里多个时间变量取值不同。
let now_ms = chrono::Local::now().timestamp_millis();
let now_s = now_ms / 1000;
let mut i = 0;
while i < bytes.len() {
if i + 1 < bytes.len() && bytes[i] == b'{' && bytes[i + 1] == b'{'
&& let Some(rel_end) = input[i + 2..].find("}}") {
let key = &input[i + 2..i + 2 + rel_end];
let resolved = match key.trim() {
"$ts" => Some(now_s.to_string()),
"$ts_ms" => Some(now_ms.to_string()),
"$uuid" => Some(uuid::Uuid::new_v4().to_string()),
k => vars
.iter()
.find(|v| v.key == k)
.map(|v| v.value.clone()),
};
match resolved {
Some(v) => out.push_str(&v),
None => out.push_str(&input[i..i + 2 + rel_end + 2]),
}
i = i + 2 + rel_end + 2;
continue;
}
// 按字符推进,避免把多字节 UTF-8 拆开
let ch = input[i..].chars().next().unwrap();
out.push(ch);
i += ch.len_utf8();
}
out
}
/// 连接前渲染遗嘱消息中的 `{{变量}}`。
/// 只作用于本次连接的配置副本,落盘的连接配置保持模板原文,
/// 这样改全局变量后下次重连即生效,`$ts` 等也按连接时刻求值。
pub fn render_will_templates(cfg: &mut ConnectionConfig, vars: &[GlobalVariable]) {
let Some(will) = cfg.last_will.as_mut() else {
return;
};
will.topic = render_template(&will.topic, vars);
will.payload = render_template(&will.payload, vars);
if let Some(v) = will.content_type.take() {
will.content_type = Some(render_template(&v, vars));
}
if let Some(v) = will.response_topic.take() {
will.response_topic = Some(render_template(&v, vars));
}
}
// ─── 日志 ────────────────────────────────────────────────────────────────────