```
feat(bookmark): 实现书签和文件夹的增删改查功能 - 添加 GetFolderDefaultRoot 方法用于获取默认根文件夹 - 实现 bookmark 和 folder 的请求/模型/响应转换函数 - 完善 CreateBookmark、CreateFolder、DeleteBookmark、DeleteFolder 等接口逻辑 - 支持删除空文件夹,非空文件夹禁止删除 - 修复根文件夹创建逻辑并添加错误处理 - 删除未实现的示例路由和无用代码 build: 引入 mage 构建工具支持多平台编译 - 添加 magefile.go 实现跨平台构建脚本 - 更新 go.mod 和 go.sum 引入 github.com/magefile/mage 依赖 - 在 README 中补充 mage 使用说明和 VSCode 配置 docs: 更新 README 并添加 mage 使用示例 - 添加 govulncheck 和 mage 构建相关文档 - 补充 VSCode gopls 配置说明 ci: 更新 .gitignore 忽略 bin 目录和可执行文件 - 添加 bin/ 到忽略列表 - 统一忽略 *.exe 和 *.sqlite3 文件 refactor(main): 优化主函数启动逻辑与日志输出 - 移除示例 helloworld 路由 - 设置 gin 为 ReleaseMode 模式 - 统一服务监听地址为变量,并增加启动日志提示 ```
This commit is contained in:
@ -3,6 +3,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"git.zzyxyz.com/zzy/zzyxyz_go_api/gen/api"
|
||||
@ -17,6 +18,71 @@ type BookMarksImpl struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
const forlder_root_id = 1
|
||||
|
||||
func (b *BookMarksImpl) GetFolderDefaultRoot(folderID *int64) (*models.Folder, error) {
|
||||
var db *gorm.DB = b.db
|
||||
var real_root_id int64 = forlder_root_id
|
||||
|
||||
// 设置默认父文件夹ID为根目录(1)
|
||||
parentID := real_root_id
|
||||
if folderID != nil && *folderID != 0 {
|
||||
parentID = *folderID
|
||||
}
|
||||
|
||||
// 检查文件夹是否存在(Find 不会在找不到记录时返回错误)
|
||||
var parentFolder models.Folder
|
||||
result := db.Limit(1).Find(&parentFolder, parentID)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
// 检查是否找到了记录
|
||||
if result.RowsAffected == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &parentFolder, nil
|
||||
}
|
||||
|
||||
func bookmarkReq2Model(req api.BookmarkRequest, parentID int64) models.Bookmark {
|
||||
return models.Bookmark{
|
||||
Name: req.Name,
|
||||
Detail: *req.Detail,
|
||||
Description: *req.Description,
|
||||
ParentPathID: parentID,
|
||||
}
|
||||
}
|
||||
|
||||
func bookmarkModel2Res(bookmark models.Bookmark) api.BookmarkResponse {
|
||||
return api.BookmarkResponse{
|
||||
Id: bookmark.ID,
|
||||
Name: bookmark.Name,
|
||||
Link: &bookmark.Link,
|
||||
Detail: &bookmark.Detail,
|
||||
Description: &bookmark.Description,
|
||||
ParentPathId: bookmark.ParentPathID,
|
||||
CreatedAt: bookmark.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func folderReq2Model(req api.FolderRequest, parentID int64) models.Folder {
|
||||
return models.Folder{
|
||||
Name: req.Name,
|
||||
ParentPathID: parentID,
|
||||
}
|
||||
}
|
||||
|
||||
func folderModel2Res(folder models.Folder) api.FolderResponse {
|
||||
return api.FolderResponse{
|
||||
Id: folder.ID,
|
||||
Name: folder.Name,
|
||||
ParentPathId: folder.ParentPathID,
|
||||
CreatedAt: folder.CreatedAt,
|
||||
UpdatedAt: folder.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func NewBookMarks(dbPath string) (*BookMarksImpl, error) {
|
||||
var err error
|
||||
var db *gorm.DB
|
||||
@ -33,15 +99,21 @@ func NewBookMarks(dbPath string) (*BookMarksImpl, error) {
|
||||
|
||||
// 创建根文件夹(如果不存在)
|
||||
var rootFolder models.Folder
|
||||
result := db.First(&rootFolder, 1)
|
||||
result := db.Limit(1).Find(&rootFolder, forlder_root_id)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
// 根文件夹不存在,创建它
|
||||
rootFolder = models.Folder{
|
||||
ID: 1,
|
||||
ID: forlder_root_id,
|
||||
Name: "Root",
|
||||
ParentPathID: 1, // 根目录指向自己
|
||||
ParentPathID: forlder_root_id, // 根目录指向自己
|
||||
}
|
||||
if err := db.Create(&rootFolder).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to create root folder: %w", err)
|
||||
}
|
||||
db.Create(&rootFolder)
|
||||
}
|
||||
|
||||
return &BookMarksImpl{db: db}, nil
|
||||
@ -59,29 +131,18 @@ func (b *BookMarksImpl) CreateBookmark(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 设置默认父文件夹ID为根目录(1)
|
||||
parentID := int64(1)
|
||||
if req.ParentPathId != nil && *req.ParentPathId != 0 {
|
||||
parentID = *req.ParentPathId
|
||||
}
|
||||
|
||||
// 检查父文件夹是否存在
|
||||
var parentFolder models.Folder
|
||||
if err := db.First(&parentFolder, parentID).Error; err != nil {
|
||||
var parentID int64
|
||||
if folder, err := b.GetFolderDefaultRoot(req.ParentPathId); err != nil || folder == nil {
|
||||
c.JSON(http.StatusNotFound, api.Error{
|
||||
Errtype: "NotFoundError",
|
||||
Message: "Parent folder not found",
|
||||
})
|
||||
return
|
||||
} else {
|
||||
parentID = folder.ID
|
||||
}
|
||||
|
||||
// 创建书签
|
||||
bookmark := models.Bookmark{
|
||||
Name: req.Name,
|
||||
Detail: *req.Detail,
|
||||
Description: *req.Description,
|
||||
ParentPathID: parentID,
|
||||
}
|
||||
bookmark := bookmarkReq2Model(req, parentID)
|
||||
|
||||
if err := db.Create(&bookmark).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
@ -91,17 +152,7 @@ func (b *BookMarksImpl) CreateBookmark(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 构造响应
|
||||
response := api.BookmarkResponse{
|
||||
Id: bookmark.ID,
|
||||
Name: bookmark.Name,
|
||||
Detail: &bookmark.Detail,
|
||||
Description: &bookmark.Description,
|
||||
ParentPathId: bookmark.ParentPathID,
|
||||
CreatedAt: bookmark.CreatedAt,
|
||||
UpdatedAt: bookmark.UpdatedAt,
|
||||
}
|
||||
|
||||
response := bookmarkModel2Res(bookmark)
|
||||
c.JSON(http.StatusCreated, response)
|
||||
}
|
||||
|
||||
@ -117,27 +168,19 @@ func (b *BookMarksImpl) CreateFolder(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 设置默认父文件夹ID为根目录(1)
|
||||
parentID := int64(1)
|
||||
if req.ParentPathId != nil && *req.ParentPathId != 0 {
|
||||
parentID = *req.ParentPathId
|
||||
}
|
||||
|
||||
// 检查父文件夹是否存在
|
||||
var parentFolder models.Folder
|
||||
if err := db.First(&parentFolder, parentID).Error; err != nil {
|
||||
var parentID int64
|
||||
if folder, err := b.GetFolderDefaultRoot(req.ParentPathId); err != nil || folder == nil {
|
||||
c.JSON(http.StatusNotFound, api.Error{
|
||||
Errtype: "NotFoundError",
|
||||
Message: "Parent folder not found",
|
||||
})
|
||||
return
|
||||
} else {
|
||||
parentID = folder.ID
|
||||
}
|
||||
|
||||
// 创建文件夹
|
||||
folder := models.Folder{
|
||||
Name: req.Name,
|
||||
ParentPathID: parentID,
|
||||
}
|
||||
folder := folderReq2Model(req, parentID)
|
||||
|
||||
if err := db.Create(&folder).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
@ -147,31 +190,94 @@ func (b *BookMarksImpl) CreateFolder(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 构造响应
|
||||
response := api.FolderResponse{
|
||||
Id: folder.ID,
|
||||
Name: folder.Name,
|
||||
ParentPathId: folder.ParentPathID,
|
||||
CreatedAt: folder.CreatedAt,
|
||||
UpdatedAt: folder.UpdatedAt,
|
||||
}
|
||||
|
||||
response := folderModel2Res(folder)
|
||||
c.JSON(http.StatusCreated, response)
|
||||
}
|
||||
|
||||
// DeleteBookmark implements api.ServerInterface.
|
||||
func (b *BookMarksImpl) DeleteBookmark(c *gin.Context, id int64) {
|
||||
panic("unimplemented")
|
||||
var db = b.db
|
||||
var bookmark models.Bookmark
|
||||
|
||||
// 查询书签是否存在
|
||||
if err := db.First(&bookmark, id).Error; err != nil {
|
||||
// FIXME maybe use 204 means already deleted status is same as delete
|
||||
c.JSON(http.StatusNotFound, api.Error{
|
||||
Errtype: "NotFoundError",
|
||||
Message: "Bookmark not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 删除书签
|
||||
if err := db.Delete(&bookmark).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "DatabaseError",
|
||||
Message: "Failed to delete bookmark",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// DeleteFolder implements api.ServerInterface.
|
||||
func (b *BookMarksImpl) DeleteFolder(c *gin.Context, id int64) {
|
||||
panic("unimplemented")
|
||||
var db = b.db
|
||||
|
||||
var folder models.Folder
|
||||
|
||||
// 查询文件夹是否存在
|
||||
if err := db.First(&folder, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, api.Error{
|
||||
Errtype: "NotFoundError",
|
||||
Message: "Folder not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if folder.ID == folder.ParentPathID {
|
||||
c.JSON(http.StatusBadRequest, api.Error{
|
||||
Errtype: "ParameterError",
|
||||
Message: "Cannot delete root folder",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 检查文件夹是否为空
|
||||
var subFolderCount int64
|
||||
db.Model(&models.Folder{}).Where("parent_path_id = ?", id).Count(&subFolderCount)
|
||||
|
||||
var bookmarkCount int64
|
||||
db.Model(&models.Bookmark{}).Where("parent_path_id = ?", id).Count(&bookmarkCount)
|
||||
|
||||
// 如果文件夹不为空,拒绝删除
|
||||
if subFolderCount > 0 || bookmarkCount > 0 {
|
||||
c.JSON(http.StatusBadRequest, api.Error{
|
||||
Errtype: "ParameterError",
|
||||
Message: "Cannot delete non-empty folder, please delete contents first",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 删除空文件夹
|
||||
if err := db.Delete(&folder).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "DatabaseError",
|
||||
Message: "Failed to delete folder",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// DeleteFolderContent implements api.ServerInterface.
|
||||
func (b *BookMarksImpl) DeleteFolderContent(c *gin.Context, id int64, params api.DeleteFolderContentParams) {
|
||||
panic("unimplemented")
|
||||
c.JSON(http.StatusNotImplemented, api.Error{
|
||||
Errtype: "error",
|
||||
Message: "Not implemented",
|
||||
})
|
||||
}
|
||||
|
||||
// GetBookmark implements api.ServerInterface.
|
||||
@ -189,17 +295,7 @@ func (b *BookMarksImpl) GetBookmark(c *gin.Context, id int64) {
|
||||
}
|
||||
|
||||
// 构造响应
|
||||
response := api.BookmarkResponse{
|
||||
Id: bookmark.ID,
|
||||
Name: bookmark.Name,
|
||||
Link: &bookmark.Link,
|
||||
Detail: &bookmark.Detail,
|
||||
Description: &bookmark.Description,
|
||||
ParentPathId: bookmark.ParentPathID,
|
||||
CreatedAt: bookmark.CreatedAt,
|
||||
UpdatedAt: bookmark.UpdatedAt,
|
||||
}
|
||||
|
||||
response := bookmarkModel2Res(bookmark)
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
@ -349,17 +445,7 @@ func (b *BookMarksImpl) UpdateBookmark(c *gin.Context, id int64) {
|
||||
}
|
||||
|
||||
// 构造响应
|
||||
response := api.BookmarkResponse{
|
||||
Id: bookmark.ID,
|
||||
Name: bookmark.Name,
|
||||
Link: &bookmark.Link,
|
||||
Detail: &bookmark.Detail,
|
||||
Description: &bookmark.Description,
|
||||
ParentPathId: bookmark.ParentPathID,
|
||||
CreatedAt: bookmark.CreatedAt,
|
||||
UpdatedAt: bookmark.UpdatedAt,
|
||||
}
|
||||
|
||||
response := bookmarkModel2Res(bookmark)
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
@ -432,16 +518,7 @@ func (b *BookMarksImpl) UpdateFolder(c *gin.Context, id int64) {
|
||||
db.Model(&models.Bookmark{}).Where("parent_path_id = ?", id).Count(&bookmarkCount)
|
||||
|
||||
// 构造响应
|
||||
response := api.FolderResponse{
|
||||
Id: folder.ID,
|
||||
Name: folder.Name,
|
||||
ParentPathId: folder.ParentPathID,
|
||||
CreatedAt: folder.CreatedAt,
|
||||
UpdatedAt: folder.UpdatedAt,
|
||||
SubFolderCount: int(subFolderCount),
|
||||
BookmarkCount: int(bookmarkCount),
|
||||
}
|
||||
|
||||
response := folderModel2Res(folder)
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user