```
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:
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
|||||||
.*/
|
.*/
|
||||||
dist/
|
dist/
|
||||||
gen/
|
gen/
|
||||||
|
bin/
|
||||||
|
|
||||||
*.exe
|
*.exe
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
|
38
README.md
38
README.md
@ -20,3 +20,41 @@ go get -tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
|
|||||||
#go:generate go tool oapi-codegen -config cfg.yaml ../../api.yaml
|
#go:generate go tool oapi-codegen -config cfg.yaml ../../api.yaml
|
||||||
go tool oapi-codegen -config cfg.yaml api.yaml
|
go tool oapi-codegen -config cfg.yaml api.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# https://go-lang.org.cn/doc/tutorial/govulncheck
|
||||||
|
go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||||
|
govulncheck ./...
|
||||||
|
|
||||||
|
go install github.com/magefile/mage@latest
|
||||||
|
mage -init
|
||||||
|
|
||||||
|
mage build
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
//go:build mage
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/magefile/mage/sh"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Runs go mod download and then installs the binary.
|
||||||
|
func Build() error {
|
||||||
|
if err := sh.Run("go", "mod", "download"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return sh.Run("go", "install", "./...")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
// on vscode settings.json
|
||||||
|
{
|
||||||
|
"gopls": {
|
||||||
|
"buildFlags": ["-tags=mage"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
1
go.mod
1
go.mod
@ -4,6 +4,7 @@ go 1.25.1
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-gonic/gin v1.10.1
|
github.com/gin-gonic/gin v1.10.1
|
||||||
|
github.com/magefile/mage v1.15.0
|
||||||
github.com/mattn/go-sqlite3 v1.14.32
|
github.com/mattn/go-sqlite3 v1.14.32
|
||||||
github.com/oapi-codegen/runtime v1.1.2
|
github.com/oapi-codegen/runtime v1.1.2
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
|
2
go.sum
2
go.sum
@ -88,6 +88,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
|
||||||
|
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.zzyxyz.com/zzy/zzyxyz_go_api/gen/api"
|
"git.zzyxyz.com/zzy/zzyxyz_go_api/gen/api"
|
||||||
@ -17,6 +18,71 @@ type BookMarksImpl struct {
|
|||||||
db *gorm.DB
|
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) {
|
func NewBookMarks(dbPath string) (*BookMarksImpl, error) {
|
||||||
var err error
|
var err error
|
||||||
var db *gorm.DB
|
var db *gorm.DB
|
||||||
@ -33,15 +99,21 @@ func NewBookMarks(dbPath string) (*BookMarksImpl, error) {
|
|||||||
|
|
||||||
// 创建根文件夹(如果不存在)
|
// 创建根文件夹(如果不存在)
|
||||||
var rootFolder models.Folder
|
var rootFolder models.Folder
|
||||||
result := db.First(&rootFolder, 1)
|
result := db.Limit(1).Find(&rootFolder, forlder_root_id)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
|
return nil, result.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
// 根文件夹不存在,创建它
|
// 根文件夹不存在,创建它
|
||||||
rootFolder = models.Folder{
|
rootFolder = models.Folder{
|
||||||
ID: 1,
|
ID: forlder_root_id,
|
||||||
Name: "Root",
|
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
|
return &BookMarksImpl{db: db}, nil
|
||||||
@ -59,29 +131,18 @@ func (b *BookMarksImpl) CreateBookmark(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置默认父文件夹ID为根目录(1)
|
var parentID int64
|
||||||
parentID := int64(1)
|
if folder, err := b.GetFolderDefaultRoot(req.ParentPathId); err != nil || folder == nil {
|
||||||
if req.ParentPathId != nil && *req.ParentPathId != 0 {
|
|
||||||
parentID = *req.ParentPathId
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查父文件夹是否存在
|
|
||||||
var parentFolder models.Folder
|
|
||||||
if err := db.First(&parentFolder, parentID).Error; err != nil {
|
|
||||||
c.JSON(http.StatusNotFound, api.Error{
|
c.JSON(http.StatusNotFound, api.Error{
|
||||||
Errtype: "NotFoundError",
|
Errtype: "NotFoundError",
|
||||||
Message: "Parent folder not found",
|
Message: "Parent folder not found",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
parentID = folder.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建书签
|
bookmark := bookmarkReq2Model(req, parentID)
|
||||||
bookmark := models.Bookmark{
|
|
||||||
Name: req.Name,
|
|
||||||
Detail: *req.Detail,
|
|
||||||
Description: *req.Description,
|
|
||||||
ParentPathID: parentID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.Create(&bookmark).Error; err != nil {
|
if err := db.Create(&bookmark).Error; err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, api.Error{
|
c.JSON(http.StatusInternalServerError, api.Error{
|
||||||
@ -91,17 +152,7 @@ func (b *BookMarksImpl) CreateBookmark(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构造响应
|
response := bookmarkModel2Res(bookmark)
|
||||||
response := api.BookmarkResponse{
|
|
||||||
Id: bookmark.ID,
|
|
||||||
Name: bookmark.Name,
|
|
||||||
Detail: &bookmark.Detail,
|
|
||||||
Description: &bookmark.Description,
|
|
||||||
ParentPathId: bookmark.ParentPathID,
|
|
||||||
CreatedAt: bookmark.CreatedAt,
|
|
||||||
UpdatedAt: bookmark.UpdatedAt,
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusCreated, response)
|
c.JSON(http.StatusCreated, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,27 +168,19 @@ func (b *BookMarksImpl) CreateFolder(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置默认父文件夹ID为根目录(1)
|
var parentID int64
|
||||||
parentID := int64(1)
|
if folder, err := b.GetFolderDefaultRoot(req.ParentPathId); err != nil || folder == nil {
|
||||||
if req.ParentPathId != nil && *req.ParentPathId != 0 {
|
|
||||||
parentID = *req.ParentPathId
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查父文件夹是否存在
|
|
||||||
var parentFolder models.Folder
|
|
||||||
if err := db.First(&parentFolder, parentID).Error; err != nil {
|
|
||||||
c.JSON(http.StatusNotFound, api.Error{
|
c.JSON(http.StatusNotFound, api.Error{
|
||||||
Errtype: "NotFoundError",
|
Errtype: "NotFoundError",
|
||||||
Message: "Parent folder not found",
|
Message: "Parent folder not found",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
parentID = folder.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建文件夹
|
// 创建文件夹
|
||||||
folder := models.Folder{
|
folder := folderReq2Model(req, parentID)
|
||||||
Name: req.Name,
|
|
||||||
ParentPathID: parentID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.Create(&folder).Error; err != nil {
|
if err := db.Create(&folder).Error; err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, api.Error{
|
c.JSON(http.StatusInternalServerError, api.Error{
|
||||||
@ -147,31 +190,94 @@ func (b *BookMarksImpl) CreateFolder(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构造响应
|
response := folderModel2Res(folder)
|
||||||
response := api.FolderResponse{
|
|
||||||
Id: folder.ID,
|
|
||||||
Name: folder.Name,
|
|
||||||
ParentPathId: folder.ParentPathID,
|
|
||||||
CreatedAt: folder.CreatedAt,
|
|
||||||
UpdatedAt: folder.UpdatedAt,
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusCreated, response)
|
c.JSON(http.StatusCreated, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteBookmark implements api.ServerInterface.
|
// DeleteBookmark implements api.ServerInterface.
|
||||||
func (b *BookMarksImpl) DeleteBookmark(c *gin.Context, id int64) {
|
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.
|
// DeleteFolder implements api.ServerInterface.
|
||||||
func (b *BookMarksImpl) DeleteFolder(c *gin.Context, id int64) {
|
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.
|
// DeleteFolderContent implements api.ServerInterface.
|
||||||
func (b *BookMarksImpl) DeleteFolderContent(c *gin.Context, id int64, params api.DeleteFolderContentParams) {
|
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.
|
// GetBookmark implements api.ServerInterface.
|
||||||
@ -189,17 +295,7 @@ func (b *BookMarksImpl) GetBookmark(c *gin.Context, id int64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 构造响应
|
// 构造响应
|
||||||
response := api.BookmarkResponse{
|
response := bookmarkModel2Res(bookmark)
|
||||||
Id: bookmark.ID,
|
|
||||||
Name: bookmark.Name,
|
|
||||||
Link: &bookmark.Link,
|
|
||||||
Detail: &bookmark.Detail,
|
|
||||||
Description: &bookmark.Description,
|
|
||||||
ParentPathId: bookmark.ParentPathID,
|
|
||||||
CreatedAt: bookmark.CreatedAt,
|
|
||||||
UpdatedAt: bookmark.UpdatedAt,
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, response)
|
c.JSON(http.StatusOK, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,17 +445,7 @@ func (b *BookMarksImpl) UpdateBookmark(c *gin.Context, id int64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 构造响应
|
// 构造响应
|
||||||
response := api.BookmarkResponse{
|
response := bookmarkModel2Res(bookmark)
|
||||||
Id: bookmark.ID,
|
|
||||||
Name: bookmark.Name,
|
|
||||||
Link: &bookmark.Link,
|
|
||||||
Detail: &bookmark.Detail,
|
|
||||||
Description: &bookmark.Description,
|
|
||||||
ParentPathId: bookmark.ParentPathID,
|
|
||||||
CreatedAt: bookmark.CreatedAt,
|
|
||||||
UpdatedAt: bookmark.UpdatedAt,
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, response)
|
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)
|
db.Model(&models.Bookmark{}).Where("parent_path_id = ?", id).Count(&bookmarkCount)
|
||||||
|
|
||||||
// 构造响应
|
// 构造响应
|
||||||
response := api.FolderResponse{
|
response := folderModel2Res(folder)
|
||||||
Id: folder.ID,
|
|
||||||
Name: folder.Name,
|
|
||||||
ParentPathId: folder.ParentPathID,
|
|
||||||
CreatedAt: folder.CreatedAt,
|
|
||||||
UpdatedAt: folder.UpdatedAt,
|
|
||||||
SubFolderCount: int(subFolderCount),
|
|
||||||
BookmarkCount: int(bookmarkCount),
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, response)
|
c.JSON(http.StatusOK, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
81
magefile.go
Normal file
81
magefile.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
//go:build mage
|
||||||
|
// +build mage
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/magefile/mage/mg" // mg contains helpful utility functions, like Deps
|
||||||
|
)
|
||||||
|
|
||||||
|
// Default target to run when none is specified
|
||||||
|
// If not set, running mage will list available targets
|
||||||
|
// var Default = Build
|
||||||
|
|
||||||
|
// A build step that requires additional params, or platform specific steps for example
|
||||||
|
// Build builds the application for multiple platforms.
|
||||||
|
func Build() error {
|
||||||
|
mg.Deps(InstallDeps)
|
||||||
|
|
||||||
|
// Define target platforms
|
||||||
|
platforms := []struct {
|
||||||
|
OS string
|
||||||
|
Arch string
|
||||||
|
}{
|
||||||
|
{"linux", "amd64"},
|
||||||
|
{"linux", "arm64"},
|
||||||
|
{"darwin", "amd64"},
|
||||||
|
{"darwin", "arm64"},
|
||||||
|
{"windows", "amd64"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range platforms {
|
||||||
|
fmt.Printf("Building for %s/%s...\n", p.OS, p.Arch)
|
||||||
|
|
||||||
|
// Set environment variables for cross-compilation
|
||||||
|
env := append(os.Environ(),
|
||||||
|
fmt.Sprintf("GOOS=%s", p.OS),
|
||||||
|
fmt.Sprintf("GOARCH=%s", p.Arch))
|
||||||
|
|
||||||
|
// Determine output name
|
||||||
|
outputName := fmt.Sprintf("./bin/zzyxyz_go_api-%s-%s", p.OS, p.Arch)
|
||||||
|
if p.OS == "windows" {
|
||||||
|
outputName += ".exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run build command
|
||||||
|
cmd := exec.Command("go", "build", "-o", outputName, ".")
|
||||||
|
cmd.Env = env
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A custom install step if you need your bin someplace other than go/bin
|
||||||
|
func Install() error {
|
||||||
|
mg.Deps(Build)
|
||||||
|
// fmt.Println("Installing...")
|
||||||
|
// return os.Rename("./MyApp", "/usr/bin/MyApp")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manage your deps, or running package managers.
|
||||||
|
func InstallDeps() error {
|
||||||
|
fmt.Println("Installing Deps...")
|
||||||
|
// cmd := exec.Command("go", "get", "github.com/stretchr/piglatin")
|
||||||
|
// return cmd.Run()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up after yourself
|
||||||
|
func Clean() {
|
||||||
|
fmt.Println("Cleaning...")
|
||||||
|
// os.RemoveAll("MyApp")
|
||||||
|
}
|
18
main.go
18
main.go
@ -16,11 +16,8 @@ import (
|
|||||||
//go:embed config/api.yaml dist/*
|
//go:embed config/api.yaml dist/*
|
||||||
var staticFiles embed.FS
|
var staticFiles embed.FS
|
||||||
|
|
||||||
func Helloworld(g *gin.Context) {
|
|
||||||
g.JSON(http.StatusOK, "helloworld")
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
|
||||||
router.GET("/ping", func(c *gin.Context) {
|
router.GET("/ping", func(c *gin.Context) {
|
||||||
@ -39,13 +36,8 @@ func main() {
|
|||||||
api.RegisterHandlers(api_router, server)
|
api.RegisterHandlers(api_router, server)
|
||||||
}
|
}
|
||||||
|
|
||||||
eg := api_router.Group("/example")
|
|
||||||
{
|
|
||||||
eg.GET("/helloworld", Helloworld)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handlers.TodoHandler(api_router)
|
handlers.TodoHandler(api_router)
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME 可能有更好的方式实现这个代码
|
// FIXME 可能有更好的方式实现这个代码
|
||||||
// 提供嵌入的静态文件访问 - OpenAPI YAML 文件和 dist 目录
|
// 提供嵌入的静态文件访问 - OpenAPI YAML 文件和 dist 目录
|
||||||
@ -66,5 +58,9 @@ func main() {
|
|||||||
// 恢复原始路径
|
// 恢复原始路径
|
||||||
r.URL.Path = originalPath
|
r.URL.Path = originalPath
|
||||||
})
|
})
|
||||||
log.Fatal(router.Run("127.0.0.1:8080"))
|
|
||||||
|
var listener = "127.0.0.1:8080"
|
||||||
|
log.Printf("Starting server at http://%s", listener)
|
||||||
|
log.Printf("Swagger UI: http://%s/swagger/index.html", listener)
|
||||||
|
log.Fatal(router.Run(listener))
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user