c8f03dd932
功能特性: - Markdown 编辑与实时预览 - 代码语法高亮 - 目录树形结构管理 - 图片粘贴上传 - Markdown 文件导入导出 - 笔记密码保护 - 前后端分离架构 技术栈: - Go + Gin + GORM + SQLite - 原生 HTML/CSS/JavaScript - Highlight.js
213 líneas
6.1 KiB
Go
213 líneas
6.1 KiB
Go
package repository
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
|
||
"gorm.io/driver/sqlite"
|
||
"gorm.io/gorm"
|
||
"note-manager/model"
|
||
)
|
||
|
||
// NoteRepository 笔记数据访问层
|
||
type NoteRepository struct {
|
||
db *gorm.DB
|
||
}
|
||
|
||
// NewNoteRepository 创建数据仓库实例,初始化数据库
|
||
func NewNoteRepository(dbPath string) (*NoteRepository, error) {
|
||
// 确保数据库目录存在
|
||
dir := filepath.Dir(dbPath)
|
||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||
return nil, fmt.Errorf("创建数据库目录失败: %w", err)
|
||
}
|
||
|
||
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||
if err != nil {
|
||
return nil, fmt.Errorf("连接数据库失败: %w", err)
|
||
}
|
||
|
||
// 自动迁移表结构
|
||
if err := db.AutoMigrate(&model.Note{}); err != nil {
|
||
return nil, fmt.Errorf("数据库迁移失败: %w", err)
|
||
}
|
||
|
||
return &NoteRepository{db: db}, nil
|
||
}
|
||
|
||
// Create 创建笔记
|
||
func (r *NoteRepository) Create(note *model.Note) error {
|
||
return r.db.Select("Title", "Content", "Category", "Tags", "Password", "IsPinned", "IsFavorite", "IsPublic", "ParentID", "IsFolder", "SortOrder").Create(note).Error
|
||
}
|
||
|
||
// GetByID 根据 ID 获取笔记
|
||
func (r *NoteRepository) GetByID(id uint) (*model.Note, error) {
|
||
var note model.Note
|
||
err := r.db.First(¬e, id).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return ¬e, nil
|
||
}
|
||
|
||
// Update 更新笔记
|
||
func (r *NoteRepository) Update(note *model.Note) error {
|
||
return r.db.Save(note).Error
|
||
}
|
||
|
||
// Delete 删除笔记
|
||
func (r *NoteRepository) Delete(id uint) error {
|
||
return r.db.Delete(&model.Note{}, id).Error
|
||
}
|
||
|
||
// ListQuery 列表查询参数
|
||
type ListQuery struct {
|
||
Page int
|
||
PageSize int
|
||
Category string
|
||
Tag string
|
||
Pinned *bool
|
||
Favorite *bool
|
||
ParentID *uint // 父目录 ID,nil 表示所有
|
||
}
|
||
|
||
// List 获取笔记列表(分页)
|
||
func (r *NoteRepository) List(q ListQuery) ([]model.NoteListItem, int64, error) {
|
||
var items []model.NoteListItem
|
||
var total int64
|
||
|
||
query := r.db.Model(&model.Note{})
|
||
|
||
if q.Category != "" {
|
||
query = query.Where("category = ?", q.Category)
|
||
}
|
||
if q.Tag != "" {
|
||
query = query.Where("tags LIKE ?", fmt.Sprintf("%%\"%s\"%%", q.Tag))
|
||
}
|
||
if q.Pinned != nil {
|
||
query = query.Where("is_pinned = ?", *q.Pinned)
|
||
}
|
||
if q.Favorite != nil {
|
||
query = query.Where("is_favorite = ?", *q.Favorite)
|
||
}
|
||
if q.ParentID != nil {
|
||
query = query.Where("parent_id = ?", *q.ParentID)
|
||
}
|
||
|
||
if err := query.Count(&total).Error; err != nil {
|
||
return nil, 0, err
|
||
}
|
||
|
||
offset := (q.Page - 1) * q.PageSize
|
||
err := query.Select("id, title, category, tags, is_pinned, is_favorite, parent_id, is_folder, sort_order, created_at, updated_at").
|
||
Order("is_folder DESC, sort_order ASC, updated_at DESC").
|
||
Offset(offset).
|
||
Limit(q.PageSize).
|
||
Find(&items).Error
|
||
|
||
return items, total, err
|
||
}
|
||
|
||
// GetAllTree 获取所有笔记的树形结构
|
||
func (r *NoteRepository) GetAllTree() ([]model.NoteListItem, error) {
|
||
var items []model.NoteListItem
|
||
err := r.db.Model(&model.Note{}).
|
||
Select("id, title, category, tags, CASE WHEN password != '' THEN 1 ELSE 0 END as has_password, is_pinned, is_favorite, is_public, parent_id, is_folder, sort_order, created_at, updated_at").
|
||
Order("is_folder DESC, sort_order ASC, title ASC").
|
||
Find(&items).Error
|
||
return items, err
|
||
}
|
||
|
||
// GetPublicTree 获取公开可见的树形结构(显示所有目录和笔记,访问时再验证密码)
|
||
func (r *NoteRepository) GetPublicTree() ([]model.NoteListItem, error) {
|
||
var items []model.NoteListItem
|
||
err := r.db.Model(&model.Note{}).
|
||
Select("id, title, category, tags, CASE WHEN password != '' THEN 1 ELSE 0 END as has_password, is_pinned, is_favorite, is_public, parent_id, is_folder, sort_order, created_at, updated_at").
|
||
Order("is_folder DESC, sort_order ASC, title ASC").
|
||
Find(&items).Error
|
||
return items, err
|
||
}
|
||
|
||
// GetByParentID 获取指定父目录下的所有项目
|
||
func (r *NoteRepository) GetByParentID(parentID uint) ([]model.NoteListItem, error) {
|
||
var items []model.NoteListItem
|
||
err := r.db.Model(&model.Note{}).
|
||
Where("parent_id = ?", parentID).
|
||
Select("id, title, category, tags, is_pinned, is_favorite, is_public, parent_id, is_folder, sort_order, created_at, updated_at").
|
||
Order("is_folder DESC, sort_order ASC, title ASC").
|
||
Find(&items).Error
|
||
return items, err
|
||
}
|
||
|
||
// GetChildrenCount 获取子项数量
|
||
func (r *NoteRepository) GetChildrenCount(parentID uint) (int64, error) {
|
||
var count int64
|
||
err := r.db.Model(&model.Note{}).Where("parent_id = ?", parentID).Count(&count).Error
|
||
return count, err
|
||
}
|
||
|
||
// DeleteWithChildren 删除目录及其下所有内容
|
||
func (r *NoteRepository) DeleteWithChildren(id uint) error {
|
||
// 先删除所有子项
|
||
if err := r.db.Where("parent_id = ?", id).Delete(&model.Note{}).Error; err != nil {
|
||
return err
|
||
}
|
||
// 再删除自己
|
||
return r.db.Delete(&model.Note{}, id).Error
|
||
}
|
||
|
||
// Search 搜索笔记(按标题和内容)
|
||
func (r *NoteRepository) Search(keyword string, page, pageSize int) ([]model.NoteListItem, int64, error) {
|
||
var items []model.NoteListItem
|
||
var total int64
|
||
|
||
like := "%" + keyword + "%"
|
||
query := r.db.Model(&model.Note{}).Where("(title LIKE ? OR content LIKE ?) AND is_folder = ?", like, like, false)
|
||
|
||
if err := query.Count(&total).Error; err != nil {
|
||
return nil, 0, err
|
||
}
|
||
|
||
offset := (page - 1) * pageSize
|
||
err := query.Select("id, title, category, tags, is_pinned, is_favorite, parent_id, is_folder, sort_order, created_at, updated_at").
|
||
Order("is_pinned DESC, updated_at DESC").
|
||
Offset(offset).
|
||
Limit(pageSize).
|
||
Find(&items).Error
|
||
|
||
return items, total, err
|
||
}
|
||
|
||
// GetCategories 获取所有分类
|
||
func (r *NoteRepository) GetCategories() ([]string, error) {
|
||
var categories []string
|
||
err := r.db.Model(&model.Note{}).
|
||
Distinct("category").
|
||
Where("category != '' AND is_folder = ?", false).
|
||
Pluck("category", &categories).Error
|
||
return categories, err
|
||
}
|
||
|
||
// GetTags 获取所有标签
|
||
func (r *NoteRepository) GetTags() ([]string, error) {
|
||
var tagsJSON []string
|
||
err := r.db.Model(&model.Note{}).
|
||
Where("tags != '' AND tags IS NOT NULL AND is_folder = ?", false).
|
||
Pluck("tags", &tagsJSON).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 去重
|
||
seen := make(map[string]bool)
|
||
var result []string
|
||
for _, t := range tagsJSON {
|
||
if !seen[t] {
|
||
seen[t] = true
|
||
result = append(result, t)
|
||
}
|
||
}
|
||
return result, nil
|
||
}
|