DesktopMemo 贡献指南
欢迎参与 DesktopMemo 项目的开发!本指南将帮助您了解如何为项目做出贡献。
📋 目录
🤝 贡献方式
您可以通过以下方式为项目做出贡献:
- 🐛 报告 Bug:在 Issues 中提交详细的问题描述
- 💡 功能建议:提出新功能或改进建议
- 📝 完善文档:改进文档、添加示例、修复错别字
- 🔧 提交代码:修复 Bug、实现新功能、优化性能
- 🌐 本地化翻译:添加或改进多语言支持
- ⭐ 推广项目:给项目加星标、分享给其他开发者
🛠️ 开发环境设置
系统要求
- 操作系统:Windows 10 版本 1903 及以上
- 架构:x86_64
- .NET SDK:.NET 9.0
- IDE:Visual Studio 2022 / VS Code / Rider(任选其一)
获取源码
# 1. Fork 本仓库到你的账号
# 2. 克隆你的 Fork
git clone https://github.com/yourname/DesktopMemo.git
cd DesktopMemo
# 3. 添加上游仓库
git remote add upstream https://github.com/SaltedDoubao/DesktopMemo.git构建项目
# 还原依赖(首次运行或更改输出目录后必须执行)
dotnet restore DesktopMemo.sln
# 调试构建
dotnet build DesktopMemo.sln --configuration Debug
# 运行应用
dotnet run --project src/DesktopMemo.App/DesktopMemo.App.csproj --configuration Debug⚠️ 重要提示:如果未更改版本号,使用
dotnet build构建项目前必须清空artifacts/vX.X.X(对应版本号)目录,避免残留文件导致编译错误。
数据目录
首次运行会在可执行文件目录生成 /.memodata:
.memodata/
├── content/ # 备忘录Markdown文件
├── .logs/ # 日志文件目录
├── settings.json # 窗口与全局设置
├── memos.db # 备忘录数据库(v2.3.0+)
└── todos.db # 待办事项数据库(v2.3.0+)📂 项目结构
DesktopMemo 采用三层架构设计:
展开
DesktopMemo_rebuild/
├── DesktopMemo.sln # Visual Studio 解决方案文件
├── Directory.Build.props # 统一的构建属性配置
├── build_exe.bat # 构建可执行文件脚本
├── LICENSE # 开源许可证
├── README.md # 项目说明(中文)
├── README_en.md # 项目说明(英文)
├── docs/ # 项目文档
│ ├── Api/ # Api文档
│ ├── ProjectStructure/ # 项目结构文档
│ ├── CONTRIBUTING.md # 贡献指南
│ └── MySQL-集成规范.md # MySQL 集成规范(中文)
├── src/ # 源代码目录
│ ├── DesktopMemo.App/ # WPF 前端应用
│ │ ├── App.xaml(.cs) # 应用启动与 DI 注册
│ │ ├── MainWindow.xaml(.cs) # 主窗口
│ │ ├── AssemblyInfo.cs # 程序集信息
│ │ ├── DesktopMemo.App.csproj # 项目文件
│ │ ├── ViewModels/ # MVVM 视图模型
│ │ │ ├── MainViewModel.cs # 主视图模型(核心逻辑)
│ │ │ ├── MemoListViewModel.cs # 备忘录列表视图模型
│ │ │ └── TodoListViewModel.cs # 待办事项视图模型
│ │ ├── Views/ # 视图组件
│ │ │ ├── ConfirmationDialog.xaml(.cs) # 通用确认对话框
│ │ │ └── ExitConfirmationDialog.xaml(.cs) # 退出确认对话框
│ │ ├── Converters/ # WPF 值转换器
│ │ │ ├── EnumToBooleanConverter.cs # 枚举到布尔值转换
│ │ │ ├── InverseBooleanToVisibilityConverter.cs # 反向布尔可见性转换
│ │ │ └── CountToVisibilityConverter.cs # 数字到可见性转换
│ │ ├── Localization/ # 多语言本地化
│ │ │ ├── LocalizationService.cs # 本地化服务
│ │ │ ├── LocalizeExtension.cs # XAML 本地化扩展
│ │ │ └── Resources/ # 资源文件
│ │ │ ├── Strings.resx # 简体中文(默认)
│ │ │ ├── Strings.en-US.resx # 英文
│ │ │ ├── Strings.zh-TW.resx # 繁体中文
│ │ │ ├── Strings.ja-JP.resx # 日文
│ │ │ └── Strings.ko-KR.resx # 韩文
│ │ ├── Resources/ # UI 资源
│ │ │ ├── Styles.xaml # 全局样式
│ │ │ ├── GlassResources.xaml # 玻璃效果资源
│ │ │ └── Themes/ # 主题
│ │ │ ├── Light.xaml # 浅色主题
│ │ │ └── Dark.xaml # 深色主题
│ │ └── Services/ # 应用层服务(预留)
│ ├── DesktopMemo.Core/ # 核心领域层(纯 .NET 库)
│ │ ├── DesktopMemo.Core.csproj # 项目文件
│ │ ├── Constants/ # 常量定义
│ │ │ └── WindowConstants.cs # 窗口相关常量
│ │ ├── Contracts/ # 契约接口
│ │ │ ├── IMemoRepository.cs # 备忘录仓储接口
│ │ │ ├── ITodoRepository.cs # 待办事项仓储接口
│ │ │ ├── ISettingsService.cs # 设置服务接口
│ │ │ ├── IWindowService.cs # 窗口服务接口
│ │ │ ├── IWindowSettingsService.cs # 窗口设置服务接口
│ │ │ ├── ITrayService.cs # 系统托盘服务接口
│ │ │ ├── ILocalizationService.cs # 本地化服务接口
│ │ │ └── IMemoSearchService.cs # 备忘录搜索服务接口
│ │ ├── Models/ # 领域模型
│ │ │ ├── Memo.cs # 备忘录模型
│ │ │ ├── TodoItem.cs # 待办事项模型
│ │ │ ├── WindowSettings.cs # 窗口设置模型
│ │ │ ├── AppTheme.cs # 应用主题枚举
│ │ │ └── SyncStatus.cs # 同步状态枚举
│ │ └── Helpers/ # 辅助工具类
│ │ ├── TransparencyHelper.cs # 透明度计算辅助类
│ │ └── DebounceHelper.cs # 防抖动辅助类
│ ├── DesktopMemo.Infrastructure/ # 基础设施层(实现层)
│ │ ├── DesktopMemo.Infrastructure.csproj # 项目文件
│ │ ├── Repositories/ # 数据仓储实现
│ │ │ ├── FileMemoRepository.cs # 基于文件的备忘录存储
│ │ │ ├── SqliteIndexedMemoRepository.cs # SQLite 备忘录存储(v2.3.0+)
│ │ │ ├── JsonTodoRepository.cs # JSON 待办事项存储
│ │ │ └── SqliteTodoRepository.cs # SQLite 待办事项存储(v2.3.0+)
│ │ └── Services/ # 服务实现
│ │ ├── JsonSettingsService.cs # JSON 设置服务
│ │ ├── MemoSearchService.cs # 备忘录搜索服务
│ │ ├── WindowService.cs # 窗口服务(Win32 API)
│ │ ├── TrayService.cs # 系统托盘服务
│ │ ├── MemoMigrationService.cs # 备忘录迁移服务
│ │ ├── MemoMetadataMigrationService.cs # 备忘录元数据迁移
│ │ └── TodoMigrationService.cs # 待办事项迁移服务
│ └── images/ # 应用图标资源
│ └── logo.ico # 应用图标
├── artifacts/ # 构建输出目录(按版本组织)
│ └── v<版本号>/
│ ├── bin/ # 二进制输出
│ └── obj/ # 中间文件
└── publish/ # 发布产物目录详细的架构说明请参阅 项目架构文档。
🔄 开发流程
1. 同步上游代码
开始开发前,确保你的 Fork 是最新的:
git fetch upstream
git checkout dev
git merge upstream/dev2. 创建功能分支
# 从 dev 分支创建新分支
git checkout -b feature/your-feature-name
# 或者修复 Bug
git checkout -b fix/bug-description分支命名建议:
feature/功能描述:新功能fix/问题描述:Bug 修复docs/文档描述:文档更新refactor/重构描述:代码重构perf/优化描述:性能优化
3. 开发和测试
- 遵循代码规范
- 添加必要的注释和文档
- 确保代码能够正常编译运行
- 在 Visual Studio 输出窗口检查调试信息
4. 提交更改
遵循提交规范编写提交信息:
git add .
git commit -m "feat: 添加新功能的简短描述"5. 推送分支
git push origin feature/your-feature-name6. 创建 Pull Request
参考 Pull Request 流程。
📝 代码规范
基本原则
- 遵循 C# 编码规范:使用 PascalCase 命名类、方法、属性,camelCase 命名局部变量
- 使用依赖注入:通过构造函数注入依赖,避免硬编码
- 保持单一职责:每个类、方法应有明确的单一职责
- 优先使用接口:面向接口编程,便于测试和扩展
异步编程规范 ⚠️
关键规范:错误的异步编程可能导致应用崩溃!
❌ 错误做法 - 可能导致死锁
private async void Button_Click(object sender, RoutedEventArgs e)
{
await SomeAsyncOperation(); // 危险!可能导致死锁
}✅ 正确做法 - Fire-and-Forget 模式
private void Button_Click(object sender, RoutedEventArgs e)
{
// 使用 fire-and-forget 模式
_ = Task.Run(async () => await HandleButtonClickAsync());
}
private async Task HandleButtonClickAsync()
{
// 异步逻辑放在这里
}UI 线程与后台线程切换
await Task.Run(async () =>
{
// 后台线程执行IO操作
await _settingsService.SaveAsync(settings);
// 切换回UI线程更新界面
await Application.Current.Dispatcher.InvokeAsync(() =>
{
ViewModel.Settings = settings;
});
});常量和辅助类使用
✅ 使用统一的常量类
using DesktopMemo.Core.Constants;
using DesktopMemo.Core.Helpers;
var opacity = WindowConstants.DEFAULT_TRANSPARENCY;
var percent = TransparencyHelper.ToPercent(opacity);❌ 避免硬编码
// 错误 - 硬编码数值
var opacity = 0.05;
var maxOpacity = 0.4;异常处理规范
try
{
await SomeAsyncOperation();
}
catch (InvalidOperationException ex)
{
// 特定异常 - 提供用户友好提示
Debug.WriteLine($"操作无效: {ex.Message}");
SetStatus("操作无效,请稍后重试");
}
catch (UnauthorizedAccessException ex)
{
// 权限问题
Debug.WriteLine($"权限不足: {ex.Message}");
SetStatus("权限不足,请检查文件访问权限");
}
catch (Exception ex)
{
// 通用异常 - 记录详细信息
Debug.WriteLine($"未知错误: {ex}");
SetStatus("操作失败,请稍后重试");
}设置保存原子性
// ✅ 原子性保存:先保存文件,成功后再更新内存
try
{
var newSettings = currentSettings with { Property = newValue };
await _settingsService.SaveAsync(newSettings); // 先保存到文件
CurrentSettings = newSettings; // 保存成功后才更新内存
}
catch (Exception ex)
{
// 保存失败,内存状态保持不变
Debug.WriteLine($"设置保存失败: {ex}");
}Win32 API 调用规范
// ✅ 安全的Win32 API调用
private static void SafeSetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
int x, int y, int cx, int cy, uint uFlags, string operation)
{
try
{
var success = SetWindowPos(hWnd, hWndInsertAfter, x, y, cx, cy, uFlags);
if (!success)
{
var error = Marshal.GetLastWin32Error();
Debug.WriteLine($"SetWindowPos失败 - 操作: {operation}, 错误代码: {error}");
}
}
catch (Exception ex)
{
Debug.WriteLine($"SetWindowPos异常 - 操作: {operation}, 异常: {ex.Message}");
}
}📋 提交规范
Commit Message 格式
<类型>(<范围>): <简短描述>
<详细描述>(可选)
<关联 Issue>(可选)类型说明
feat: 新功能fix: Bug 修复docs: 文档更新style: 代码格式(不影响功能)refactor: 重构(既不是新增功能也不是修复 Bug)perf: 性能优化test: 测试相关chore: 构建过程或辅助工具的变动
示例
feat(memo): 添加备忘录全文搜索功能
实现基于 SQLite FTS5 的全文搜索,支持关键词高亮显示。
Closes #123fix(settings): 修复透明度保存失败问题
使用原子性保存模式,确保设置文件先保存成功后再更新内存状态。
Fixes #456🔀 Pull Request 流程
1. 创建 Pull Request
在 GitHub 上创建 Pull Request,目标分支通常是 dev。
2. PR 描述模板
## 变更类型
- [ ] 新功能
- [ ] Bug 修复
- [ ] 文档更新
- [ ] 代码重构
- [ ] 性能优化
## 变更说明
<!-- 简要描述你的变更 -->
## 需求背景
<!-- 为什么需要这个变更?解决了什么问题? -->
## 变更摘要
<!-- 列出主要的代码变更 -->
-
-
## 测试验证
<!-- 如何验证这个变更?包括测试步骤和结果 -->
- [ ] 本地编译通过
- [ ] 功能测试通过
- [ ] 无明显性能问题
## 相关 Issue
<!-- 关联的 Issue 编号 -->
Closes #
## 截图/录屏
<!-- 如果是 UI 变更,请附上截图或录屏 -->
## 其他说明
<!-- 其他需要说明的内容 -->3. Code Review
- 耐心等待维护者的代码审查
- 根据反馈及时修改代码
- 保持友好和专业的沟通
4. 合并
- PR 被批准后,维护者会将其合并到主分支
- 删除你的功能分支(可选)
✅ 测试要求
基本验证
提交 PR 前,请确保:
# 1. 清理旧的构建产物(如果版本号未变)
# 手动删除 artifacts/vX.X.X 目录
# 2. 构建验证
dotnet clean DesktopMemo.sln
dotnet restore DesktopMemo.sln
dotnet build DesktopMemo.sln --configuration Debug
# 3. 运行应用
dotnet run --project src/DesktopMemo.App/DesktopMemo.App.csproj --configuration Debug功能测试
- 测试你的新功能或 Bug 修复是否正常工作
- 确保没有破坏现有功能
- 检查是否有内存泄漏或性能问题
- 在不同窗口状态下测试(置顶、透明度、穿透模式等)
调试技巧
- 查看调试输出:在 Visual Studio 输出窗口查看
Debug.WriteLine信息 - 检查数据文件:查看
.memodata目录下的数据库和配置文件 - 性能分析:使用 Visual Studio 诊断工具监控内存和 CPU
📚 文档维护
需要更新文档的情况
| 变更类型 | 需要更新的文档 |
|---|---|
| 新增模块/功能 | - docs/ProjectStructure/01_架构图.md- docs/ProjectStructure/02_模块划分.md- README.md |
| 升级依赖或更换技术 | - docs/ProjectStructure/03_技术栈和依赖.md |
| 修改业务流程 | - docs/ProjectStructure/04_数据流和通信.md |
| 架构调整 | - docs/ProjectStructure/01_架构图.md- docs/ProjectStructure/02_模块划分.md |
| 开发规范变更 | - CONTRIBUTING.md / Dev/ / project_structure/ / api/ 等相关文档 |
| 新增/修改本地化 | - src/DesktopMemo.App/Localization/Resources/ 下的资源文件 |
文档编写规范
- 使用 Markdown 格式
- 保持中英文混排时的空格规范
- 添加必要的代码示例和图表
- 更新文档底部的"最后更新"日期
❓ 常见问题
编译问题
Q: NETSDK1005:找不到 project.assets.json
A: 先运行 dotnet restore DesktopMemo.sln,必要时删除 artifacts/ 目录后重新构建。
Q: Assembly 特性重复
A: 删除 artifacts/ 目录后重新 restore + build。
UI 问题
Q: 数据绑定未刷新
A: 检查是否调用 OnPropertyChanged,确认 ViewModel 继承自 ObservableObject。
Q: 对话框无法显示
A: 检查 Owner 设置是否正确,确保在 UI 线程上创建对话框。
设置问题
Q: 透明度重启后变为 0% 或 100%
A: 检查 /.memodata/settings.json 文件完整性,验证是否正确使用 TransparencyHelper。
Q: 设置保存失败导致数据不一致
A: 确保使用原子性保存模式:先保存文件,成功后再更新内存状态。
崩溃问题
Q: 勾选"不再显示"后程序卡顿崩溃
A: 检查是否使用了 async void 事件处理器,改用 Task.Run + fire-and-forget 模式。
Q: Win32 API 调用失败
A: 检查 API 返回值,使用 Marshal.GetLastWin32Error() 获取错误代码,添加错误处理。
版本管理
Q: 如何更新版本号?
A: 需要同步更新以下位置:
DesktopMemo.App/DesktopMemo.App.csproj中的<Version>、<AssemblyVersion>、<FileVersion>Directory.Build.props里的ArtifactsVersion默认值MainViewModel.InitializeAppInfo()中的回退字符串README.md等说明文档
数据迁移
Q: 如何调试数据迁移功能?
A:
- 备份
.memodata目录 - 删除目标数据库文件(如
memos.db) - 准备旧格式的数据文件
- 运行应用,查看 Visual Studio 输出窗口的迁移日志
🌟 行为准则
- 尊重他人:保持友好和专业的沟通方式
- 建设性反馈:提供有帮助的建议和批评
- 包容性:欢迎不同背景和技能水平的贡献者
- 耐心:理解每个人都有学习曲线
📧 联系方式
- 项目维护者:SaltedDoubao
- 报告问题:GitHub Issues
- 功能建议:GitHub Issues
- 讨论交流:GitHub Discussions
📖 相关文档
🙏 致谢
感谢所有为 DesktopMemo 做出贡献的开发者!
最后更新:2025-11-17