add:Dlass联动

This commit is contained in:
CJK_mkp
2025-11-02 10:11:15 +08:00
parent 4ef77c2e72
commit 4fb7031060
7 changed files with 521 additions and 21 deletions
+82
View File
@@ -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
}
}
/// <summary>
/// 上传笔记文件
/// </summary>
/// <param name="endpoint">上传端点</param>
/// <param name="filePath">文件路径</param>
/// <param name="boardId">白板ID</param>
/// <param name="secretKey">白板密钥</param>
/// <param name="title">笔记标题(可选)</param>
/// <param name="description">笔记描述(可选)</param>
/// <param name="tags">笔记标签(可选)</param>
public async Task<T> UploadNoteAsync<T>(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<T>(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);
}
}
/// <summary>
/// 释放资源
/// </summary>
+200
View File
@@ -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
{
/// <summary>
/// Dlass笔记自动上传辅助类
/// </summary>
public class DlassNoteUploader
{
private const string APP_ID = "app_WkjocWqsrVY7T6zQV2CfiA";
private const string APP_SECRET = "o7dx5b5ASGUMcM72PCpmRQYAhSijqaOVHoGyBK0IxbA";
/// <summary>
/// 上传笔记响应模型
/// </summary>
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; }
}
/// <summary>
/// 白板信息模型(用于查找白板)
/// </summary>
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; }
}
/// <summary>
/// 认证响应模型
/// </summary>
private class AuthWithTokenResponse
{
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("whiteboards")]
public List<WhiteboardInfo> Whiteboards { get; set; }
}
/// <summary>
/// 异步上传PNG文件到Dlass
/// </summary>
/// <param name="pngFilePath">PNG文件路径</param>
/// <returns>是否上传成功</returns>
public static async Task<bool> 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<AuthWithTokenResponse>("/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<UploadNoteResponse>(
"/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;
}
}
}
}
@@ -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)
{
}
});
}
}
+24
View File
@@ -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)
{
}
});
}
// 获取日期文件夹路径
+6
View File
@@ -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;
}
}
+95 -21
View File
@@ -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"/>
<!-- 用户Token设置 -->
<TextBlock Text="用户Token"
FontSize="16"
FontWeight="SemiBold"
Foreground="{StaticResource TextForeground}"
Margin="0,0,0,8"/>
Margin="0,0,0,1"/>
<TextBlock Text="设置您的用户Token以访问Dlass服务端功能。您可以从Dlass平台获取您的用户Token。"
FontSize="12"
Foreground="{StaticResource TextSecondary}"
TextWrapping="Wrap"
Margin="0,0,0,12"/>
Margin="0,0,0,2"/>
<ui:SimpleStackPanel Orientation="Vertical" Spacing="8">
<ui:SimpleStackPanel Orientation="Vertical" Spacing="4">
<ComboBox x:Name="CmbSavedTokens"
FontSize="14"
Padding="12,8"
@@ -137,7 +137,7 @@
<TextBox x:Name="TxtNewToken"
FontSize="14"
Padding="12,8"
Padding="12,4"
Background="#18181b"
Foreground="{StaticResource TextForeground}"
BorderBrush="{StaticResource BorderBrush}"
@@ -280,24 +280,38 @@
Margin="0,4,0,0"/>
</ui:SimpleStackPanel>
<!-- 连接状态 -->
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="12" Margin="0,4,0,0">
<TextBlock Text="连接状态:"
FontSize="14"
Foreground="{StaticResource TextForeground}"
VerticalAlignment="Center"
Width="100"/>
<TextBlock x:Name="TxtConnectionStatus"
Text="未连接"
FontSize="14"
Foreground="{StaticResource TextSecondary}"
VerticalAlignment="Center"/>
</ui:SimpleStackPanel>
<Line HorizontalAlignment="Stretch"
X1="0" Y1="0" X2="1" Y2="0"
Stroke="{StaticResource BorderBrush}"
StrokeThickness="1"
Margin="0,12,0,8"/>
Margin="0,2,0,1"/>
<!-- 班级选择 -->
<TextBlock Text="班级选择"
FontSize="16"
FontWeight="SemiBold"
Foreground="{StaticResource TextForeground}"
Margin="0,0,0,8"/>
Margin="0,0,0,1"/>
<TextBlock Text="连接成功后,将自动加载可用班级列表。"
FontSize="12"
Foreground="{StaticResource TextSecondary}"
TextWrapping="Wrap"
Margin="0,0,0,8"/>
Margin="0,0,0,2"/>
<ComboBox x:Name="CmbClassSelection"
FontSize="14"
@@ -316,27 +330,87 @@
X1="0" Y1="0" X2="1" Y2="0"
Stroke="{StaticResource BorderBrush}"
StrokeThickness="1"
Margin="0,12,0,8"/>
Margin="0,2,0,1"/>
<!-- 连接状态 -->
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="12">
<TextBlock Text="连接状态:"
FontSize="14"
Foreground="{StaticResource TextForeground}"
VerticalAlignment="Center"
Width="100"/>
<TextBlock x:Name="TxtConnectionStatus"
Text="未连接"
FontSize="14"
<!-- 自动上传设置 -->
<TextBlock Text="自动上传设置"
FontSize="16"
FontWeight="SemiBold"
Foreground="{StaticResource TextForeground}"
Margin="0,0,0,1"/>
<ui:SimpleStackPanel Orientation="Vertical" Spacing="4">
<ui:ToggleSwitch x:Name="ToggleSwitchAutoUploadNotes"
Header="自动上传PNG笔记"
FontSize="14"
Foreground="{StaticResource TextForeground}"
Toggled="ToggleSwitchAutoUploadNotes_Toggled">
</ui:ToggleSwitch>
<TextBlock Text="启用后,保存的PNG截图将自动上传到所选班级的白板。"
FontSize="12"
Foreground="{StaticResource TextSecondary}"
VerticalAlignment="Center"/>
TextWrapping="Wrap"
Margin="0,0,0,2"/>
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="12"
IsEnabled="{Binding ElementName=ToggleSwitchAutoUploadNotes, Path=IsOn}">
<TextBlock Text="上传延迟时间:"
FontSize="14"
Foreground="{StaticResource TextForeground}"
VerticalAlignment="Center"
Width="120"/>
<TextBox x:Name="TxtUploadDelayMinutes"
FontSize="14"
Background="#18181b"
Foreground="{StaticResource TextForeground}"
BorderBrush="{StaticResource BorderBrush}"
BorderThickness="1"
MinWidth="100"
Height="32"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Left"
TextChanged="TxtUploadDelayMinutes_TextChanged"
PreviewTextInput="TxtUploadDelayMinutes_PreviewTextInput">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="6">
<ScrollViewer x:Name="PART_ContentHost"
Margin="12,0"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TextBox.Style>
</TextBox>
<TextBlock Text="分钟"
FontSize="14"
Foreground="{StaticResource TextSecondary}"
VerticalAlignment="Center"
Margin="0,0,0,0"/>
</ui:SimpleStackPanel>
<TextBlock Text="设置上传延迟时间(0-60分钟),可以在保存后等待一段时间再上传。"
FontSize="12"
Foreground="{StaticResource TextSecondary}"
TextWrapping="Wrap"
Margin="0,2,0,0"/>
</ui:SimpleStackPanel>
<Line HorizontalAlignment="Stretch"
X1="0" Y1="0" X2="1" Y2="0"
Stroke="{StaticResource BorderBrush}"
StrokeThickness="1"
Margin="0,8,0,12"/>
Margin="0,2,0,1"/>
<!-- 操作按钮 -->
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="12" Margin="0,8,0,0">
@@ -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
}
}
/// <summary>
/// 加载自动上传设置
/// </summary>
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);
}
}
/// <summary>
/// 自动上传开关切换事件
/// </summary>
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);
}
}
/// <summary>
/// 上传延迟时间输入框文本改变事件
/// </summary>
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);
}
}
/// <summary>
/// 上传延迟时间输入框预览文本输入事件(只允许数字)
/// </summary>
private void TxtUploadDelayMinutes_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex("[^0-9]+");
e.Handled = regex.IsMatch(e.Text);
}
/// <summary>
/// 标题栏拖动事件
/// </summary>