From efc1f5fbae97a50199c4cb41db7bf41b44cf8f77 Mon Sep 17 00:00:00 2001 From: Cxx0822 <1556464090@qq.com> Date: Wed, 27 May 2026 11:26:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=AE=9E=E6=88=98=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/.vitepress/config.mts | 3 +- docs/.vitepress/theme/router/index.ts | 37 ++- docs/DevOps/index.md | 4 +- docs/Practice/Company/Learn.md | 68 ++++ docs/Practice/index.md | 10 + docs/Web/Network/Mqtt.md | 56 ++++ docs/Web/Network/NetCore.md | 311 ++++++++++++++++++ .../Netword-Program.md => Network/Summary.md} | 307 ----------------- docs/Web/{Others => Vue}/Browser-Process.md | 0 docs/Web/Vue/ElTable.md | 202 ++++++++++++ docs/Web/{Others => Vue}/ElUpload.md | 0 docs/Web/{Others => Vue}/JavaScript-Guide.md | 0 docs/Web/{Others => Vue}/Package.md | 0 docs/Web/{Others => Vue}/Vite-Principle.md | 2 +- docs/index.md | 5 +- 15 files changed, 687 insertions(+), 318 deletions(-) create mode 100644 docs/Practice/Company/Learn.md create mode 100644 docs/Practice/index.md create mode 100644 docs/Web/Network/Mqtt.md create mode 100644 docs/Web/Network/NetCore.md rename docs/Web/{Others/Netword-Program.md => Network/Summary.md} (76%) rename docs/Web/{Others => Vue}/Browser-Process.md (100%) create mode 100644 docs/Web/Vue/ElTable.md rename docs/Web/{Others => Vue}/ElUpload.md (100%) rename docs/Web/{Others => Vue}/JavaScript-Guide.md (100%) rename docs/Web/{Others => Vue}/Package.md (100%) rename docs/Web/{Others => Vue}/Vite-Principle.md (99%) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 0d68571..a969c9a 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -32,7 +32,8 @@ export default withMermaid({ sidebar: { '/Web/': [routers[0]], '/DevOps/': [routers[1]], - '/Others/': [routers[2]] + '/Practice/': [routers[2]], + '/Others/': [routers[3]] }, socialLinks: [ diff --git a/docs/.vitepress/theme/router/index.ts b/docs/.vitepress/theme/router/index.ts index e72d629..00bb6f1 100644 --- a/docs/.vitepress/theme/router/index.ts +++ b/docs/.vitepress/theme/router/index.ts @@ -6,7 +6,13 @@ export const routers = [ text: '🌿 Vue', items: [ { text: 'Vue3-Common', link: '/Web/Vue/Vue3-Common' }, - { text: 'Vue3架构原理', link: '/Web/Vue/Vue3-Architecture' } + { text: 'Vue3架构原理', link: '/Web/Vue/Vue3-Architecture' }, + { text: 'JavaScript知识点整理', link: '/Web/Vue/JavaScript-Guide' }, + { text: '前端包管理器', link: '/Web/Vue/Package' }, + { text: 'Vite核心原理', link: '/Web/Vue/Vite-Principle' }, + { text: '浏览器渲染全流程解析', link: '/Web/Vue/Browser-Process' }, + { text: 'Element Plus 上传器', link: '/Web/Vue/ElUpload' }, + { text: 'Element Plus 表格组件', link: '/Web/Vue/ElTable' }, ] }, { @@ -43,6 +49,14 @@ export const routers = [ { text: 'SQL高阶用法', link: '/Web/MySQL/SQL-Advance' } ] }, + { + text: '📶 Network', + items: [ + { text: '网络编程简介', link: '/Web/Network/Summary' }, + { text: '.Net Core实现', link: '/Web/Network/NetCore' }, + { text: 'Mqtt简介与使用', link: '/Web/Network/Mqtt' } + ] + }, { text: '📱 Flutter', items: [ @@ -55,11 +69,6 @@ export const routers = [ { text: '🔄 其他', items: [ - { text: 'JavaScript知识点整理', link: '/Web/Others/JavaScript-Guide' }, - { text: '前端包管理器', link: '/Web/Others/Package' }, - { text: 'Vite核心原理', link: '/Web/Others/Vite-Principle' }, - { text: '浏览器渲染全流程解析', link: '/Web/Others/Browser-Process' }, - { text: 'Element Plus 上传器', link: '/Web/Others/ElUpload' }, { text: 'RustFS简介和使用', link: '/Web/Others/RustFS' }, { text: 'AList简介和使用', link: '/Web/Others/AList' }, { text: 'InfluxDB简介和使用', link: '/Web/Others/InfluxDB' }, @@ -79,6 +88,22 @@ export const routers = [ { text: 'Linux命令', link: '/DevOps/LinuxCommand' } ] }, + { + text: '🏗️ 项目实战', + items: [ + { + text: '🏢 公司项目', + items: [ + { text: '埃学习', link: '/Practice/Company/Learn' } + ] + }, + { + text: '👤 个人项目', + items: [ + ] + }, + ] + }, { text: '🔄 其他', items: [ diff --git a/docs/DevOps/index.md b/docs/DevOps/index.md index 3bdbe65..0821e44 100644 --- a/docs/DevOps/index.md +++ b/docs/DevOps/index.md @@ -1,10 +1,10 @@ --- layout: doc -title: DevOps +title: 运维部署 --- - \ No newline at end of file + \ No newline at end of file diff --git a/docs/Practice/Company/Learn.md b/docs/Practice/Company/Learn.md new file mode 100644 index 0000000..3729e40 --- /dev/null +++ b/docs/Practice/Company/Learn.md @@ -0,0 +1,68 @@ +--- + title: 埃学习 + date: 2026-05-27 +--- + +# 一、项目背景 +  为解决企业内部线下培训成本高、进度难追踪的问题,开发了一套企业在线学习平台,用于员工课程学习、在线考试、培训计划管理与数据统计分析,已全面接入企业微信工作台。 + +# 二、系统架构 + +# 三、核心模块 +## 3.1 员工端 +1. 课程学习(视频播放、进度记录、课程评论) +2. 在线考试(随机试卷、自动阅卷) +3. 学习记录和统计 + +## 3.2 管理员端 +1. 课程管理(视频上传、章节管理) +2. 培训计划管理(按部门/岗位下发) +3. 考试与题库管理 +4. 人员与组织管理 +5. 员工学习数据统计 + +# 四、技术栈 +| 层级 | 技术 | +|---|---| +| **前端** | Vue 3、Element Plus、Vue Router、Pinia、DPlayer (视频播放)、@vue-office (文档预览)、ECharts (数据可视化)| +| **后端** | Spring Boot 3、Spring MVC、Sa-Token (鉴权)、MyBatis-Plus | +| **中间件** | Redis、企业微信 SDK | +| **存储** | MySQL 8.0 (业务数据)、AList (视频/文件资源存储) | +| **视频处理** | Java JAVE 2 (FFmpeg 封装,视频压缩转码) | +| **运维** | Docker、Fluent Bit (日志采集)、OpenObserver (日志可视化) | + +# 五、难点/亮点 +## 5.1 公网视频播放卡顿 +* 问题 +  服务器公网带宽小,高清视频并发播放严重卡顿。 + +* 方案 +  上传视频后,使用JAVE2调用FFmpeg将视频统一压缩为720p的清晰度视频。 + +* 效果 +  视频体积减少60%+,公网环境播放流畅。 + +## 5.2 学习进度防作弊 +* 问题 +  员工学习视频课程时,可通过拖动进度条快速完成课程,无法真实反映学习行为,存在“刷课”风险。 + +* 方案 +  根据管理员设定的课程学习时长,在员工学习时,启动定时器;每分钟会保存学习时长至数据库中,下次学习时,直接跳转至上次学习进度;课程是否完成已定时器结束为准。 + +* 效果 +  有效防止通过拖拽进度条刷课,保证学习时长真实可信;同时支持断点续学,避免重复学习,满足企业培训考核与合规审计要求。 + +## 5.3 学习计划通知 +* 问题 +  培训计划、考试通知无法及时触达员工,导致培训参与率低、管理成本高。 + +* 方案 +  在发布培训计划时,调用企业微信消息推送接口,自动向指定员工发送应用消息通知;结合定时任务,对未完成培训的人员进行周期性提醒。 + +* 效果 +  培训通知实时触达,未读提醒显著提高培训完成率;管理员无需手动维护人员信息,降低了系统运维成本。 + +# 六、成果 +1. 平台全面替代线下培训,年度培训计划完成率由 60% 提升至 95% 以上,满足了企业合规培训要求。 +2. 过视频压缩方案解决了公网带宽瓶颈,结合企业微信自动化通知,大幅降低人工催办成本,整体培训运营成本下降约 40%。 +3. 实现了学习进度防作弊与学习记录全跟踪,为公司提供精准的数据统计报表,支撑人才盘点与绩效评估。 diff --git a/docs/Practice/index.md b/docs/Practice/index.md new file mode 100644 index 0000000..8c32826 --- /dev/null +++ b/docs/Practice/index.md @@ -0,0 +1,10 @@ +--- +layout: doc +title: 项目实战 +--- + + + + \ No newline at end of file diff --git a/docs/Web/Network/Mqtt.md b/docs/Web/Network/Mqtt.md new file mode 100644 index 0000000..f1b9b09 --- /dev/null +++ b/docs/Web/Network/Mqtt.md @@ -0,0 +1,56 @@ +--- + title: Mqtt简介与使用 + date: 2026-05-27 +--- + +# 一、简介 +  MQTT(Message Queuing Telemetry Transport)​ 是一种 轻量级物联网通信协议,基于 发布 / 订阅(Pub/Sub)​ 模式。 + +## 1.1 核心概念 +1. Broker:消息服务器 +2. Client:发布或订阅消息的设备 / 程序 +3. Topic: 消息主题(路径形式) +4. Publish:发布消息 +5. Subscribe:订阅消息 +6. QoS:服务质量等级(0 / 1 / 2) + +## 1.2 工作方式 +```text +[设备A] --发布--> TopicA --> [Broker] --> TopicA --> [设备B] +``` + +# 二、安装 +  [EMQX](https://www.emqx.com/zh) Enterprise​ 是基于开源 EMQX 扩展的企业级 MQTT 物联网消息接入平台,完全兼容 MQTT v3.1.1 / v5.0。 + +## 2.1 docker安装 +```yml +services: + emqx: + image: emqx/emqx-enterprise:5.10.0 + container_name: emqx + ports: + - "1883:1883" + - "8083:8083" + - "8084:8084" + - "8883:8883" + - "18083:18083" + restart: always +``` + +  启动容器后,输入ip:18083,默认账号密码为admin/public。 + +# 三、消息规范 +## 3.1 Topic +```text +//// +``` + +  例如`iot/thermostat/dev001/status/online`、`iot/thermostat/dev001/cmd/set_mode`。 + +## 通配符 +1. +:单层通配符,例如:iot/+/dev001/telemetry/+ +2. #:多层通配符,只能放在topic末尾,例如:iot/thermostat/dev001/# + +::: tip +使用+通配符时,必须严格满足层级要求,例如,如果topic是5层,那么iot/+将无法匹配到消息 +::: \ No newline at end of file diff --git a/docs/Web/Network/NetCore.md b/docs/Web/Network/NetCore.md new file mode 100644 index 0000000..dec36fa --- /dev/null +++ b/docs/Web/Network/NetCore.md @@ -0,0 +1,311 @@ +--- + title: .Net Core实现 + date: 2026-05-27 +--- + +# 一、.Net Core实现 +## 1.1 SuperSocket +  [SuperSocket](https://www.supersocket.net/)是一个轻量级, 跨平台而且可扩展的 .Net/Mono Socket 服务器程序框架。可以轻松构建TCP、UDP、WebSocket服务器。 + +## 1.2 安装依赖 +  NuGut安装SuperSocket、SuperSocket.WebSocket和SuperSocket.WebSocket.Server 2.0及以上版本。 + +## 1.3 配置文件 +  appsettings.json +```json +{ + "serverOptions": { + "TcpServer": { + "name": "TcpServer", + "listeners": [ + { + "ip": "Any", + "port": 4040 + } + ] + }, + "WebSocketServer": { + "name": "WebSocket", + "listeners": [ + { + "ip": "Any", + "port": 5050 + } + ] + } + }, + "Mqtt": { + "Host": "127.0.0.1", + "Port": 1883, + "ClientId": "MqttClient", + "Topics": [ + "test/topic1", + "test/topic2" + ] + } +} +``` + +## 1.4 主程序 +  program.cs +```cs +var host = Host.CreateDefaultBuilder(args) + .ConfigureServices((context, services) => + { + services.AddSingleton, TcpPackageHandler>(); + services.AddSingleton(); + + // 1. 注入 MQTT 配置 + services.Configure(context.Configuration.GetSection("Mqtt")); + // 2. 注册 MQTT 客户端 + services.AddSingleton(serviceProvider => new MqttClientFactory().CreateMqttClient()); + // 3. 注册 MQTT 后台服务 + services.AddHostedService(); + }) + .AsMultipleServerHostBuilder() + .AddServer(builder => + { + builder.ConfigureServerOptions((ctx, config) => config.GetSection("TcpServer")); + }) + .AddWebSocketServer(builder => + { + builder + .UseWebSocketMessageHandler(async (session, package) => + { + using var scope = session.Server.ServiceProvider.CreateScope(); + var handler = scope.ServiceProvider.GetRequiredService(); + + await handler.HandleAsync(session, package); + }) + .ConfigureServerOptions((ctx, config) => config.GetSection("WebSocketServer")); + }) + .ConfigureLogging(logging => logging.ClearProviders()) + .UseNLog() + .Build(); + +await host.RunAsync(); +``` + +  通过.AsMultipleServerHostBuilder()可以构造多服务器实例。 + +# 二、 TCP服务器 +## 2.1 协议数据 +```cs +/// +/// 协议数据结构 示例:65 6D 00 05 00 01 68 65 6C 6C 6F +/// +/// 帧头标识 +/// 数据长度 +/// 数据类型 +/// 数据内容 +/// +record ProtocolFrame( + ushort Magic, + ushort Length, + ushort Type, + byte[] Payload +); +``` + +  可以根据实际情况自定义消息格式。 + +## 2.2 协议解析 +```cs +class BinaryPipelineFilter : FixedHeaderPipelineFilter +{ + /// + /// 固定头长度 + /// + public BinaryPipelineFilter() : base(6) + { + } + + /// + /// 从包头中解析出 Body 长度 + /// + /// 字节流 + /// Body 长度 + protected override int GetBodyLengthFromHeader(ref ReadOnlySequence buffer) + { + var reader = new SequenceReader(buffer); + + // 读取前2字节 → magic + reader.TryReadBigEndian(out ushort magic); + + // 再读2字节 → Length + reader.TryReadBigEndian(out ushort length); + + // 再读2字节 → Type + reader.TryReadBigEndian(out ushort type); + + // 校验帧头 + if (magic != 0x656D) + { + throw new Exception("非法帧头"); + } + + // 限制长度 + if (length == 0 || length > 8192) + { + throw new Exception("非法长度"); + } + + // 返回 Payload 长度 + return length; + } + + /// + /// 把完整字节包 → 转换成 ProtocolFrame + /// + /// 字节流 + /// ProtocolFrame + protected override ProtocolFrame DecodePackage(ref ReadOnlySequence buffer) + { + var reader = new SequenceReader(buffer); + + // 读取前2字节 → magic + reader.TryReadBigEndian(out ushort magic); + + // 再读2字节 → Length + reader.TryReadBigEndian(out ushort length); + + // 再读2字节 → Type + reader.TryReadBigEndian(out ushort type); + + var payload = buffer.Slice(6, length).ToArray(); + + // 构造ProtocolFrame + return new ProtocolFrame(magic, length, type, payload); + } +} +``` + +  根据协议数据ProtocolFrame来解包。 + +## 2.3 消息处理 +```cs +class TcpPackageHandler(ILogger logger) : IPackageHandler +{ + private readonly ILogger _logger = logger; + + public async ValueTask Handle(IAppSession session, ProtocolFrame package, CancellationToken cancellationToken) + { + _logger.LogInformation($"Magic={package.Magic:X4}, Type={package.Type}, Len={package.Length}"); + await session.SendAsync(package.Payload, cancellationToken); + } +} +``` + +## 2.4 TCPService +```cs +class TcpService(IServiceProvider serviceProvider, IOptions serverOptions) : SuperSocketService(serviceProvider, serverOptions) +{ +} +``` + +# 三、Websocket服务器 +## 3.1 消息处理 +```cs +class WebSocketMessageHandler(ILogger logger) +{ + private readonly ILogger _logger = logger; + + public async ValueTask HandleAsync(WebSocketSession session, WebSocketPackage package) + { + _logger.LogInformation($"[WebSocket] {package.Message}"); + await session.SendAsync("ok"); + } +} +``` + +# 四、 MQTT服务器 +## 4.1 MqttService +```cs +class MqttService(IMqttClient mqttClient, IOptions settings, ILogger logger) : BackgroundService +{ + private readonly IMqttClient _mqttClient = mqttClient; + private readonly MqttSettings _settings = settings.Value; + private readonly ILogger _logger = logger; + private MqttClientOptions? _mqttOptions; + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + // 构建连接配置 + _mqttOptions = new MqttClientOptionsBuilder() + .WithClientId($"{_settings.ClientId}_{Guid.NewGuid():N}") + .WithTcpServer(_settings.Host, _settings.Port) + .WithCleanSession() + .Build(); + + _mqttClient.ConnectedAsync += OnConnectedAsync; + _mqttClient.DisconnectedAsync += OnDisconnectedAsync; + _mqttClient.ApplicationMessageReceivedAsync += HandleMessage; + + _logger.LogInformation("[MQTT] 正在连接到服务器 {Host}:{Port}...", _settings.Host, _settings.Port); + + await _mqttClient.ConnectAsync(_mqttOptions, stoppingToken); + + // 等待程序停止 + await Task.Delay(Timeout.Infinite, stoppingToken); + } + + public async Task OnConnectedAsync(MqttClientConnectedEventArgs arg) + { + _logger.LogInformation("[MQTT] 已成功连接到服务器"); + + // 连接成功后订阅主题 + foreach (var topic in _settings.Topics) + { + await _mqttClient.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce); + _logger.LogInformation($"[MQTT] 已订阅主题:{topic}"); + } + + await Task.CompletedTask; + } + + public async Task OnDisconnectedAsync(MqttClientDisconnectedEventArgs arg) + { + _logger.LogError("[MQTT] 连接失败,原因:{Reason}", arg.Reason); + + await Task.CompletedTask; + } + + public async Task HandleMessage(MqttApplicationMessageReceivedEventArgs arg) + { + var topic = arg.ApplicationMessage.Topic; + var payload = Encoding.UTF8.GetString(arg.ApplicationMessage.Payload); + + _logger.LogInformation($"\n[MQTT] 收到消息\n主题:{topic}\n内容:{payload}\n"); + + await Task.CompletedTask; + } + + public async Task PublishAsync(string topic, string payload, CancellationToken cancellationToken = default) + { + try + { + var message = new MqttApplicationMessageBuilder() + .WithTopic(topic) + .WithPayload(payload) + .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce) + .Build(); + + await _mqttClient.PublishAsync(message, cancellationToken); + return true; + } + catch (Exception ex) + { + _logger.LogError(ex, "[MQTT] 发布消息到主题 {Topic} 失败", topic); + return false; + } + } + + public override async Task StopAsync(CancellationToken stoppingToken) + { + await _mqttClient.DisconnectAsync(); + await base.StopAsync(stoppingToken); + } +} +``` + +  需要实现后台服务接口一直运行。 \ No newline at end of file diff --git a/docs/Web/Others/Netword-Program.md b/docs/Web/Network/Summary.md similarity index 76% rename from docs/Web/Others/Netword-Program.md rename to docs/Web/Network/Summary.md index 271c883..1479feb 100644 --- a/docs/Web/Others/Netword-Program.md +++ b/docs/Web/Network/Summary.md @@ -634,310 +634,3 @@ Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= # 服务器加密后的密 | 固定头部 | 必选,2 字节起,包含消息类型(如发布、订阅)、QoS 等级、是否保留消息等标识。 | | 可变头部 | 可选,仅部分消息类型(如发布、订阅)需要,包含主题名、消息 ID 等信息。 | | 负载 | 可选,消息的实际内容(如 JSON 字符串、二进制数据),例如 {"temperature": 25}。 | - -# 五、.Net Core实现 -## 5.1 SuperSocket -  [SuperSocket](https://www.supersocket.net/)是一个轻量级, 跨平台而且可扩展的 .Net/Mono Socket 服务器程序框架。可以轻松构建TCP、UDP、WebSocket服务器。 - -## 5.2 安装依赖 -  NuGut安装SuperSocket、SuperSocket.WebSocket和SuperSocket.WebSocket.Server 2.0及以上版本。 - -## 5.3 配置文件 -  appsettings.json -```json -{ - "serverOptions": { - "TcpServer": { - "name": "TcpServer", - "listeners": [ - { - "ip": "Any", - "port": 4040 - } - ] - }, - "WebSocketServer": { - "name": "WebSocket", - "listeners": [ - { - "ip": "Any", - "port": 5050 - } - ] - } - }, - "Mqtt": { - "Host": "127.0.0.1", - "Port": 1883, - "ClientId": "MqttClient", - "Topics": [ - "test/topic1", - "test/topic2" - ] - } -} -``` - -## 5.3 主程序 -  program.cs -```cs -var host = Host.CreateDefaultBuilder(args) - .ConfigureServices((context, services) => - { - services.AddSingleton, TcpPackageHandler>(); - services.AddSingleton(); - - // 1. 注入 MQTT 配置 - services.Configure(context.Configuration.GetSection("Mqtt")); - // 2. 注册 MQTT 客户端 - services.AddSingleton(serviceProvider => new MqttClientFactory().CreateMqttClient()); - // 3. 注册 MQTT 后台服务 - services.AddHostedService(); - }) - .AsMultipleServerHostBuilder() - .AddServer(builder => - { - builder.ConfigureServerOptions((ctx, config) => config.GetSection("TcpServer")); - }) - .AddWebSocketServer(builder => - { - builder - .UseWebSocketMessageHandler(async (session, package) => - { - using var scope = session.Server.ServiceProvider.CreateScope(); - var handler = scope.ServiceProvider.GetRequiredService(); - - await handler.HandleAsync(session, package); - }) - .ConfigureServerOptions((ctx, config) => config.GetSection("WebSocketServer")); - }) - .ConfigureLogging(logging => logging.ClearProviders()) - .UseNLog() - .Build(); - -await host.RunAsync(); -``` - -  通过.AsMultipleServerHostBuilder()可以构造多服务器实例。 - -## 5.4 TCP服务器 -### 5.4.1 协议数据 -```cs -/// -/// 协议数据结构 示例:65 6D 00 05 00 01 68 65 6C 6C 6F -/// -/// 帧头标识 -/// 数据长度 -/// 数据类型 -/// 数据内容 -/// -record ProtocolFrame( - ushort Magic, - ushort Length, - ushort Type, - byte[] Payload -); -``` - -  可以根据实际情况自定义消息格式。 - -### 5.4.2 协议解析 -```cs -class BinaryPipelineFilter : FixedHeaderPipelineFilter -{ - /// - /// 固定头长度 - /// - public BinaryPipelineFilter() : base(6) - { - } - - /// - /// 从包头中解析出 Body 长度 - /// - /// 字节流 - /// Body 长度 - protected override int GetBodyLengthFromHeader(ref ReadOnlySequence buffer) - { - var reader = new SequenceReader(buffer); - - // 读取前2字节 → magic - reader.TryReadBigEndian(out ushort magic); - - // 再读2字节 → Length - reader.TryReadBigEndian(out ushort length); - - // 再读2字节 → Type - reader.TryReadBigEndian(out ushort type); - - // 校验帧头 - if (magic != 0x656D) - { - throw new Exception("非法帧头"); - } - - // 限制长度 - if (length == 0 || length > 8192) - { - throw new Exception("非法长度"); - } - - // 返回 Payload 长度 - return length; - } - - /// - /// 把完整字节包 → 转换成 ProtocolFrame - /// - /// 字节流 - /// ProtocolFrame - protected override ProtocolFrame DecodePackage(ref ReadOnlySequence buffer) - { - var reader = new SequenceReader(buffer); - - // 读取前2字节 → magic - reader.TryReadBigEndian(out ushort magic); - - // 再读2字节 → Length - reader.TryReadBigEndian(out ushort length); - - // 再读2字节 → Type - reader.TryReadBigEndian(out ushort type); - - var payload = buffer.Slice(6, length).ToArray(); - - // 构造ProtocolFrame - return new ProtocolFrame(magic, length, type, payload); - } -} -``` - -  根据协议数据ProtocolFrame来解包。 - -### 5.4.3 消息处理 -```cs -class TcpPackageHandler(ILogger logger) : IPackageHandler -{ - private readonly ILogger _logger = logger; - - public async ValueTask Handle(IAppSession session, ProtocolFrame package, CancellationToken cancellationToken) - { - _logger.LogInformation($"Magic={package.Magic:X4}, Type={package.Type}, Len={package.Length}"); - await session.SendAsync(package.Payload, cancellationToken); - } -} -``` - -### 5.4.4 TCPService -```cs -class TcpService(IServiceProvider serviceProvider, IOptions serverOptions) : SuperSocketService(serviceProvider, serverOptions) -{ -} -``` - -## 5.5 Websocket服务器 -### 5.5.1 消息处理 -```cs -class WebSocketMessageHandler(ILogger logger) -{ - private readonly ILogger _logger = logger; - - public async ValueTask HandleAsync(WebSocketSession session, WebSocketPackage package) - { - _logger.LogInformation($"[WebSocket] {package.Message}"); - await session.SendAsync("ok"); - } -} -``` - -## 5.6 MQTT服务器 -### 5.6 MqttService -```cs -class MqttService(IMqttClient mqttClient, IOptions settings, ILogger logger) : BackgroundService -{ - private readonly IMqttClient _mqttClient = mqttClient; - private readonly MqttSettings _settings = settings.Value; - private readonly ILogger _logger = logger; - private MqttClientOptions? _mqttOptions; - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - // 构建连接配置 - _mqttOptions = new MqttClientOptionsBuilder() - .WithClientId($"{_settings.ClientId}_{Guid.NewGuid():N}") - .WithTcpServer(_settings.Host, _settings.Port) - .WithCleanSession() - .Build(); - - _mqttClient.ConnectedAsync += OnConnectedAsync; - _mqttClient.DisconnectedAsync += OnDisconnectedAsync; - _mqttClient.ApplicationMessageReceivedAsync += HandleMessage; - - _logger.LogInformation("[MQTT] 正在连接到服务器 {Host}:{Port}...", _settings.Host, _settings.Port); - - await _mqttClient.ConnectAsync(_mqttOptions, stoppingToken); - - // 等待程序停止 - await Task.Delay(Timeout.Infinite, stoppingToken); - } - - public async Task OnConnectedAsync(MqttClientConnectedEventArgs arg) - { - _logger.LogInformation("[MQTT] 已成功连接到服务器"); - - // 连接成功后订阅主题 - foreach (var topic in _settings.Topics) - { - await _mqttClient.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce); - _logger.LogInformation($"[MQTT] 已订阅主题:{topic}"); - } - - await Task.CompletedTask; - } - - public async Task OnDisconnectedAsync(MqttClientDisconnectedEventArgs arg) - { - _logger.LogError("[MQTT] 连接失败,原因:{Reason}", arg.Reason); - - await Task.CompletedTask; - } - - public async Task HandleMessage(MqttApplicationMessageReceivedEventArgs arg) - { - var topic = arg.ApplicationMessage.Topic; - var payload = Encoding.UTF8.GetString(arg.ApplicationMessage.Payload); - - _logger.LogInformation($"\n[MQTT] 收到消息\n主题:{topic}\n内容:{payload}\n"); - - await Task.CompletedTask; - } - - public async Task PublishAsync(string topic, string payload, CancellationToken cancellationToken = default) - { - try - { - var message = new MqttApplicationMessageBuilder() - .WithTopic(topic) - .WithPayload(payload) - .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce) - .Build(); - - await _mqttClient.PublishAsync(message, cancellationToken); - return true; - } - catch (Exception ex) - { - _logger.LogError(ex, "[MQTT] 发布消息到主题 {Topic} 失败", topic); - return false; - } - } - - public override async Task StopAsync(CancellationToken stoppingToken) - { - await _mqttClient.DisconnectAsync(); - await base.StopAsync(stoppingToken); - } -} -``` - -  需要实现后台服务接口一直运行。 \ No newline at end of file diff --git a/docs/Web/Others/Browser-Process.md b/docs/Web/Vue/Browser-Process.md similarity index 100% rename from docs/Web/Others/Browser-Process.md rename to docs/Web/Vue/Browser-Process.md diff --git a/docs/Web/Vue/ElTable.md b/docs/Web/Vue/ElTable.md new file mode 100644 index 0000000..fae4a35 --- /dev/null +++ b/docs/Web/Vue/ElTable.md @@ -0,0 +1,202 @@ +--- + title: Element Plus 表格组件 + date: 2026-05-27 +--- + +# 一、加载中 +```vue + + + +``` + +::: tip +要配合pinia存储使用,获取数据前将loading置为true,完成后置为false。 +::: + +# 二、展开行 +```vue + + + +``` + +  通过类型expand和插槽props实现,可以展示任意格式数据。 +  增加表格的`row-key`、`expand-row-key`属性可以实现只展开某一行功能。 + +# 三、排序和筛选 +```vue + + + +``` + +  在`el-table-column`中添加`sortable`即可实现排序功能。 +  在`el-table-column`中添加`filters`和`filter-method`实现筛选功能。 +  `filters`为原始数据中该字段下的所有值,并转为指定格式,也可以自定义筛选字段。 + +# 四、表头搜索 +```vue + + + +``` + +  表头搜索主要利用`header`插槽和`el-popover`组件实现。 \ No newline at end of file diff --git a/docs/Web/Others/ElUpload.md b/docs/Web/Vue/ElUpload.md similarity index 100% rename from docs/Web/Others/ElUpload.md rename to docs/Web/Vue/ElUpload.md diff --git a/docs/Web/Others/JavaScript-Guide.md b/docs/Web/Vue/JavaScript-Guide.md similarity index 100% rename from docs/Web/Others/JavaScript-Guide.md rename to docs/Web/Vue/JavaScript-Guide.md diff --git a/docs/Web/Others/Package.md b/docs/Web/Vue/Package.md similarity index 100% rename from docs/Web/Others/Package.md rename to docs/Web/Vue/Package.md diff --git a/docs/Web/Others/Vite-Principle.md b/docs/Web/Vue/Vite-Principle.md similarity index 99% rename from docs/Web/Others/Vite-Principle.md rename to docs/Web/Vue/Vite-Principle.md index 4a84c8f..486ed6c 100644 --- a/docs/Web/Others/Vite-Principle.md +++ b/docs/Web/Vue/Vite-Principle.md @@ -1,5 +1,5 @@ --- - title: 前端包管理器 + title: Vite核心原理 date: 2025-12-13 --- diff --git a/docs/index.md b/docs/index.md index 43417ae..f011f51 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,8 +13,11 @@ hero: text: Web平台 link: /Web/ - theme: brand - text: DevOps + text: 运维部署 link: /DevOps/ + - theme: brand + text: 项目实战 + link: /Practice/ features: - title: 🚀 技术分享