From 4fb70310609364e270f2bea27cd48a588279632a Mon Sep 17 00:00:00 2001 From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com> Date: Sun, 2 Nov 2025 10:11:15 +0800 Subject: [PATCH] =?UTF-8?q?add:Dlass=E8=81=94=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/Helpers/DlassApiClient.cs | 82 +++++++ Ink Canvas/Helpers/DlassNoteUploader.cs | 200 ++++++++++++++++++ .../MainWindow_cs/MW_Save&OpenStrokes.cs | 18 ++ Ink Canvas/MainWindow_cs/MW_Screenshot.cs | 24 +++ Ink Canvas/Resources/Settings.cs | 6 + Ink Canvas/Windows/DlassSettingsWindow.xaml | 116 ++++++++-- .../Windows/DlassSettingsWindow.xaml.cs | 96 +++++++++ 7 files changed, 521 insertions(+), 21 deletions(-) create mode 100644 Ink Canvas/Helpers/DlassNoteUploader.cs diff --git a/Ink Canvas/Helpers/DlassApiClient.cs b/Ink Canvas/Helpers/DlassApiClient.cs index 438c4947..61a04f42 100644 --- a/Ink Canvas/Helpers/DlassApiClient.cs +++ b/Ink Canvas/Helpers/DlassApiClient.cs @@ -1,7 +1,9 @@ using Ink_Canvas.Helpers; using Newtonsoft.Json; using System; +using System.IO; using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; @@ -343,6 +345,86 @@ namespace Ink_Canvas.Helpers } } + /// + /// 上传笔记文件 + /// + /// 上传端点 + /// 文件路径 + /// 白板ID + /// 白板密钥 + /// 笔记标题(可选) + /// 笔记描述(可选) + /// 笔记标签(可选) + public async Task UploadNoteAsync(string endpoint, string filePath, string boardId, string secretKey, string title = null, string description = null, string tags = null) + { + try + { + if (!File.Exists(filePath)) + { + throw new FileNotFoundException($"文件不存在: {filePath}"); + } + + var request = new HttpRequestMessage(HttpMethod.Post, endpoint); + + // 设置白板认证头 + request.Headers.Add("X-Board-ID", boardId); + request.Headers.Add("X-Secret-Key", secretKey); + + // 创建multipart/form-data内容 + var content = new MultipartFormDataContent(); + + // 添加文件 + var fileContent = new ByteArrayContent(File.ReadAllBytes(filePath)); + var fileName = Path.GetFileName(filePath); + fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); + content.Add(fileContent, "file", fileName); + + // 添加可选参数 + if (!string.IsNullOrEmpty(title)) + { + content.Add(new StringContent(title), "title"); + } + if (!string.IsNullOrEmpty(description)) + { + content.Add(new StringContent(description), "description"); + } + if (!string.IsNullOrEmpty(tags)) + { + content.Add(new StringContent(tags), "tags"); + } + + request.Content = content; + + var response = await _httpClient.SendAsync(request); + var responseContent = await response.Content.ReadAsStringAsync(); + + if (response.IsSuccessStatusCode) + { + if (string.IsNullOrEmpty(responseContent)) + { + return default(T); + } + return JsonConvert.DeserializeObject(responseContent); + } + else + { + throw new Exception($"上传文件失败: {response.StatusCode} - {responseContent}"); + } + } + catch (HttpRequestException httpEx) + { + throw new Exception($"上传文件时网络错误: {httpEx.Message}", httpEx); + } + catch (TaskCanceledException timeoutEx) + { + throw new Exception($"上传文件超时: {endpoint}", timeoutEx); + } + catch (Exception ex) + { + throw new Exception($"上传文件时出错: {ex.Message}", ex); + } + } + /// /// 释放资源 /// diff --git a/Ink Canvas/Helpers/DlassNoteUploader.cs b/Ink Canvas/Helpers/DlassNoteUploader.cs new file mode 100644 index 00000000..ed08fece --- /dev/null +++ b/Ink Canvas/Helpers/DlassNoteUploader.cs @@ -0,0 +1,200 @@ +using Ink_Canvas.Helpers; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Ink_Canvas.Helpers +{ + /// + /// Dlass笔记自动上传辅助类 + /// + public class DlassNoteUploader + { + private const string APP_ID = "app_WkjocWqsrVY7T6zQV2CfiA"; + private const string APP_SECRET = "o7dx5b5ASGUMcM72PCpmRQYAhSijqaOVHoGyBK0IxbA"; + + /// + /// 上传笔记响应模型 + /// + public class UploadNoteResponse + { + [JsonProperty("success")] + public bool Success { get; set; } + + [JsonProperty("message")] + public string Message { get; set; } + + [JsonProperty("note_id")] + public int? NoteId { get; set; } + + [JsonProperty("filename")] + public string Filename { get; set; } + + [JsonProperty("file_path")] + public string FilePath { get; set; } + + [JsonProperty("file_url")] + public string FileUrl { get; set; } + } + + /// + /// 白板信息模型(用于查找白板) + /// + private class WhiteboardInfo + { + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("board_id")] + public string BoardId { get; set; } + + [JsonProperty("secret_key")] + public string SecretKey { get; set; } + + [JsonProperty("class_name")] + public string ClassName { get; set; } + + [JsonProperty("class_id")] + public int ClassId { get; set; } + } + + /// + /// 认证响应模型 + /// + private class AuthWithTokenResponse + { + [JsonProperty("success")] + public bool Success { get; set; } + + [JsonProperty("whiteboards")] + public List Whiteboards { get; set; } + } + + /// + /// 异步上传PNG文件到Dlass + /// + /// PNG文件路径 + /// 是否上传成功 + public static async Task UploadPngNoteAsync(string pngFilePath) + { + try + { + // 检查是否启用自动上传 + if (MainWindow.Settings?.Dlass?.IsAutoUploadNotes != true) + { + return false; + } + + // 检查文件是否存在 + if (!File.Exists(pngFilePath)) + { + LogHelper.WriteLogToFile($"上传失败:文件不存在 - {pngFilePath}", LogHelper.LogType.Error); + return false; + } + + // 检查文件大小(最大10MB) + var fileInfo = new FileInfo(pngFilePath); + if (fileInfo.Length > 10 * 1024 * 1024) + { + LogHelper.WriteLogToFile($"上传失败:文件过大({fileInfo.Length / 1024 / 1024}MB),超过10MB限制", LogHelper.LogType.Error); + return false; + } + + // 获取设置的班级名称 + var selectedClassName = MainWindow.Settings?.Dlass?.SelectedClassName; + if (string.IsNullOrEmpty(selectedClassName)) + { + LogHelper.WriteLogToFile("上传失败:未选择班级", LogHelper.LogType.Error); + return false; + } + + // 获取用户Token + var userToken = MainWindow.Settings?.Dlass?.UserToken; + if (string.IsNullOrEmpty(userToken)) + { + LogHelper.WriteLogToFile("上传失败:未设置用户Token", LogHelper.LogType.Error); + return false; + } + + // 获取API基础URL + var apiBaseUrl = MainWindow.Settings?.Dlass?.ApiBaseUrl ?? "https://dlass.tech"; + + // 创建API客户端并获取白板信息 + DlassApiClient apiClient = null; + try + { + apiClient = new DlassApiClient(APP_ID, APP_SECRET, apiBaseUrl, userToken); + + // 调用认证接口获取白板列表 + var authData = new + { + app_id = APP_ID, + app_secret = APP_SECRET, + user_token = userToken + }; + + var authResult = await apiClient.PostAsync("/api/whiteboard/framework/auth-with-token", authData, requireAuth: false); + + if (authResult == null || !authResult.Success || authResult.Whiteboards == null) + { + LogHelper.WriteLogToFile("上传失败:无法获取白板信息", LogHelper.LogType.Error); + return false; + } + + // 查找匹配班级的白板 + var whiteboard = authResult.Whiteboards + .FirstOrDefault(w => !string.IsNullOrEmpty(w.ClassName) && w.ClassName == selectedClassName); + + if (whiteboard == null || string.IsNullOrEmpty(whiteboard.BoardId) || string.IsNullOrEmpty(whiteboard.SecretKey)) + { + LogHelper.WriteLogToFile($"上传失败:未找到班级'{selectedClassName}'对应的白板", LogHelper.LogType.Error); + return false; + } + + // 准备上传参数 + var fileName = Path.GetFileNameWithoutExtension(pngFilePath); + var title = fileName; + var description = $"自动上传的笔记 - {DateTime.Now:yyyy-MM-dd HH:mm:ss}"; + var tags = "自动上传,笔记"; + + // 上传文件 + var uploadResult = await apiClient.UploadNoteAsync( + "/api/whiteboard/upload_note", + pngFilePath, + whiteboard.BoardId, + whiteboard.SecretKey, + title, + description, + tags); + + if (uploadResult != null && uploadResult.Success) + { + LogHelper.WriteLogToFile($"笔记上传成功:{fileName} -> {uploadResult.FileUrl}", LogHelper.LogType.Event); + return true; + } + else + { + LogHelper.WriteLogToFile($"上传失败:服务器响应失败 - {uploadResult?.Message ?? "未知错误"}", LogHelper.LogType.Error); + return false; + } + } + finally + { + apiClient?.Dispose(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"上传笔记时出错: {ex.Message}", LogHelper.LogType.Error); + return false; + } + } + } +} + diff --git a/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs b/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs index b0aae681..bcbc487c 100644 --- a/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs +++ b/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs @@ -7,6 +7,7 @@ using System.Drawing.Imaging; using System.IO; using System.IO.Compression; using System.Text; +using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Forms; @@ -310,6 +311,23 @@ namespace Ink_Canvas var fs = new FileStream(savePathWithName, FileMode.Create); inkCanvas.Strokes.Save(fs); fs.Close(); + + _ = Task.Run(async () => + { + try + { + var delayMinutes = Settings?.Dlass?.AutoUploadDelayMinutes ?? 0; + if (delayMinutes > 0) + { + await Task.Delay(TimeSpan.FromMinutes(delayMinutes)); + } + + await Helpers.DlassNoteUploader.UploadPngNoteAsync(imagePathWithName); + } + catch (Exception) + { + } + }); } } diff --git a/Ink Canvas/MainWindow_cs/MW_Screenshot.cs b/Ink Canvas/MainWindow_cs/MW_Screenshot.cs index f398a4e9..844c0982 100644 --- a/Ink Canvas/MainWindow_cs/MW_Screenshot.cs +++ b/Ink Canvas/MainWindow_cs/MW_Screenshot.cs @@ -3,6 +3,7 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; +using System.Threading.Tasks; using System.Windows; using System.Windows.Forms; @@ -65,6 +66,29 @@ namespace Ink_Canvas { ShowNotification($"截图成功保存至 {savePath}"); } + _ = Task.Run(async () => + { + try + { + var delayMinutes = Settings?.Dlass?.AutoUploadDelayMinutes ?? 0; + if (delayMinutes > 0) + { + await Task.Delay(TimeSpan.FromMinutes(delayMinutes)); + } + + var uploaded = await Helpers.DlassNoteUploader.UploadPngNoteAsync(savePath); + if (uploaded && !isHideNotification) + { + Dispatcher.Invoke(() => + { + ShowNotification($"笔记已自动上传到Dlass"); + }); + } + } + catch (Exception) + { + } + }); } // 获取日期文件夹路径 diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs index 4ec614da..3941e2ac 100644 --- a/Ink Canvas/Resources/Settings.cs +++ b/Ink Canvas/Resources/Settings.cs @@ -740,5 +740,11 @@ namespace Ink_Canvas [JsonProperty("apiBaseUrl")] public string ApiBaseUrl { get; set; } = "https://dlass.tech"; + + [JsonProperty("isAutoUploadNotes")] + public bool IsAutoUploadNotes { get; set; } = false; + + [JsonProperty("autoUploadDelayMinutes")] + public int AutoUploadDelayMinutes { get; set; } = 0; } } diff --git a/Ink Canvas/Windows/DlassSettingsWindow.xaml b/Ink Canvas/Windows/DlassSettingsWindow.xaml index a8e998ee..fe993dd6 100644 --- a/Ink Canvas/Windows/DlassSettingsWindow.xaml +++ b/Ink Canvas/Windows/DlassSettingsWindow.xaml @@ -106,22 +106,22 @@ X1="0" Y1="0" X2="1" Y2="0" Stroke="{StaticResource BorderBrush}" StrokeThickness="1" - Margin="0,8,0,12"/> + Margin="0,2,0,2"/> + Margin="0,0,0,1"/> + Margin="0,0,0,2"/> - + + + + + + + + Margin="0,2,0,1"/> + Margin="0,0,0,1"/> + Margin="0,0,0,2"/> + Margin="0,2,0,1"/> - - - - + + + + + + + + TextWrapping="Wrap" + Margin="0,0,0,2"/> + + + + + + + + + + + + + Margin="0,2,0,1"/> diff --git a/Ink Canvas/Windows/DlassSettingsWindow.xaml.cs b/Ink Canvas/Windows/DlassSettingsWindow.xaml.cs index 494e17d9..a68d7640 100644 --- a/Ink Canvas/Windows/DlassSettingsWindow.xaml.cs +++ b/Ink Canvas/Windows/DlassSettingsWindow.xaml.cs @@ -2,6 +2,7 @@ using Ink_Canvas.Helpers; using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; @@ -34,6 +35,9 @@ namespace Ink_Canvas.Windows // 加载保存的token LoadUserToken(); + // 加载自动上传设置 + LoadAutoUploadSettings(); + // 初始化API客户端(优先使用用户token) InitializeApiClient(); @@ -272,6 +276,98 @@ namespace Ink_Canvas.Windows } } + /// + /// 加载自动上传设置 + /// + private void LoadAutoUploadSettings() + { + try + { + if (MainWindow.Settings?.Dlass != null) + { + ToggleSwitchAutoUploadNotes.IsOn = MainWindow.Settings.Dlass.IsAutoUploadNotes; + var delayMinutes = MainWindow.Settings.Dlass.AutoUploadDelayMinutes; + if (delayMinutes < 0 || delayMinutes > 60) + { + delayMinutes = 0; + } + TxtUploadDelayMinutes.Text = delayMinutes.ToString(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"加载自动上传设置时出错: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 自动上传开关切换事件 + /// + private void ToggleSwitchAutoUploadNotes_Toggled(object sender, RoutedEventArgs e) + { + try + { + if (MainWindow.Settings?.Dlass != null) + { + MainWindow.Settings.Dlass.IsAutoUploadNotes = ToggleSwitchAutoUploadNotes.IsOn; + MainWindow.SaveSettingsToFile(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"保存自动上传设置时出错: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 上传延迟时间输入框文本改变事件 + /// + private void TxtUploadDelayMinutes_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) + { + try + { + if (MainWindow.Settings?.Dlass != null && int.TryParse(TxtUploadDelayMinutes.Text, out int delayMinutes)) + { + // 限制范围在0-60分钟 + if (delayMinutes < 0) + { + delayMinutes = 0; + TxtUploadDelayMinutes.Text = "0"; + } + else if (delayMinutes > 60) + { + delayMinutes = 60; + TxtUploadDelayMinutes.Text = "60"; + } + + MainWindow.Settings.Dlass.AutoUploadDelayMinutes = delayMinutes; + MainWindow.SaveSettingsToFile(); + } + else if (string.IsNullOrWhiteSpace(TxtUploadDelayMinutes.Text)) + { + // 空文本时设置为0 + if (MainWindow.Settings?.Dlass != null) + { + MainWindow.Settings.Dlass.AutoUploadDelayMinutes = 0; + MainWindow.SaveSettingsToFile(); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"保存上传延迟时间时出错: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 上传延迟时间输入框预览文本输入事件(只允许数字) + /// + private void TxtUploadDelayMinutes_PreviewTextInput(object sender, TextCompositionEventArgs e) + { + Regex regex = new Regex("[^0-9]+"); + e.Handled = regex.IsMatch(e.Text); + } + /// /// 标题栏拖动事件 ///