当前位置: 首页 > wzjs >正文

wordpress商城网站seo推广小分享

wordpress商城网站,seo推广小分享,在word上怎么做网站链接,邢台企业网站制作建设AI书签管理工具开发全记录(十七):Sun-Panel书签同步实现 1. 前言 📝 在上一篇文章中,Sun-Panel的接口分析,本篇文章将聚焦于sun-panel数据同步服务的实现。 2. Sun-Panel API分析 📊 Sun-Pa…

AI书签管理工具开发全记录(十七):Sun-Panel书签同步实现

1. 前言 📝

在上一篇文章中,Sun-Panel的接口分析,本篇文章将聚焦于sun-panel数据同步服务的实现。

2. Sun-Panel API分析 📊

Sun-Panel提供了完善的API接口,我们需要实现以下核心功能:

功能端点方法描述
登录/api/loginPOST获取认证token
获取分组/api/panel/itemIconGroup/getListPOST获取所有书签分组
获取书签/api/panel/itemIcon/getListByGroupIdPOST获取指定分组的书签
创建分组/api/panel/itemIconGroup/editPOST创建新分组
创建书签/api/panel/itemIcon/editPOST创建新书签

3. 同步功能设计 🔄

3.1 同步策略

我们提供三种同步模式:

  1. Pull模式:从Sun-Panel拉取数据到本地
  2. Push模式:将本地数据推送到Sun-Panel
  3. Sync模式:双向同步,确保两端数据一致

这里一致只保证分组和title一致,只关注最核心的数据同步

3.2 数据模型映射

本地数据模型与Sun-Panel模型的对应关系:

本地模型Sun-Panel模型说明
CategoryGroup书签分类/分组
BookmarkItemIcon书签项

3.3 同步流程

3.3.1 同步流程
pull
push
sync
开始
选择模式
从Sun-Panel获取数据
推送本地数据
双向同步
处理分组
处理书签
保存到本地
遍历本地分类
创建远程分组
推送书签
拉取远程数据
推送本地数据
3.3.2 验证token流程
连接失败
连接成功
HTTP错误
API错误
登录成功
无效
有效
开始配置验证
用户输入URL
用户输入用户名
用户输入密码
尝试连接
显示错误信息
允许重试?
退出配置
发送登录请求
验证响应
显示网络错误
解析错误信息
显示API错误
获取Token
保存Token到配置
测试Token有效性
Token有效?
清除Token
显示Token无效
获取用户信息
显示验证成功
保存配置
结束流程

4. 代码实现 👨‍💻

4.1 Sun-Panel客户端实现

首先创建Sun-Panel客户端,封装所有API调用:

// internal/sunpanel/client.gopackage sunpanelimport ("bytes""encoding/json""fmt""net/http"
)type Client struct {baseURL stringtoken   stringclient  *http.Client
}type LoginResponse struct {Code int `json:"code"`Data struct {Token string `json:"token"`} `json:"data"`Msg string `json:"msg"`
}type Group struct {ID          int    `json:"id"`Title       string `json:"title"`Description string `json:"description"`
}type Bookmark struct {ID              int    `json:"id"`Title           string `json:"title"`URL             string `json:"url"`Description     string `json:"description"`ItemIconGroupId int    `json:"itemIconGroupId"`
}type GroupListResponse struct {Code int `json:"code"`Data struct {List []Group `json:"list"`} `json:"data"`Msg string `json:"msg"`
}type BookmarkListResponse struct {Code int `json:"code"`Data struct {List []Bookmark `json:"list"`} `json:"data"`Msg string `json:"msg"`
}type UserInfo struct {ID           int    `json:"id"`Username     string `json:"username"`Name         string `json:"name"`HeadImage    string `json:"headImage"`Status       int    `json:"status"`Role         int    `json:"role"`Mail         string `json:"mail"`ReferralCode string `json:"referralCode"`
}type AuthInfoResponse struct {Code int `json:"code"`Data struct {User      UserInfo `json:"user"`VisitMode int      `json:"visitMode"`} `json:"data"`Msg string `json:"msg"`
}func NewClient(baseURL string) *Client {return &Client{baseURL: baseURL,client:  &http.Client{},}
}func (c *Client) Login(username, password string) (string, error) {url := fmt.Sprintf("%s/api/login", c.baseURL)data := map[string]string{"username": username,"password": password,}jsonData, err := json.Marshal(data)if err != nil {return "", err}resp, err := c.client.Post(url, "application/json", bytes.NewBuffer(jsonData))if err != nil {return "", err}defer resp.Body.Close()var loginResp LoginResponseif err := json.NewDecoder(resp.Body).Decode(&loginResp); err != nil {return "", err}if loginResp.Code != 0 {return "", fmt.Errorf("login failed: %s", loginResp.Msg)}c.token = loginResp.Data.Tokenreturn loginResp.Data.Token, nil
}func (c *Client) SetToken(token string) {c.token = token
}func (c *Client) GetGroups() ([]Group, error) {url := fmt.Sprintf("%s/api/panel/itemIconGroup/getList", c.baseURL)req, err := http.NewRequest("POST", url, nil)if err != nil {return nil, err}req.Header.Set("Authorization", "Bearer "+c.token)req.Header.Set("Token", c.token)req.Header.Set("Content-Type", "application/json")resp, err := c.client.Do(req)if err != nil {return nil, err}defer resp.Body.Close()var groupResp GroupListResponseif err := json.NewDecoder(resp.Body).Decode(&groupResp); err != nil {return nil, err}if groupResp.Code != 0 {return nil, fmt.Errorf("get groups failed: %s", groupResp.Msg)}return groupResp.Data.List, nil
}func (c *Client) GetBookmarksByGroup(groupID int) ([]Bookmark, error) {url := fmt.Sprintf("%s/api/panel/itemIcon/getListByGroupId", c.baseURL)data := map[string]int{"itemIconGroupId": groupID,}jsonData, err := json.Marshal(data)if err != nil {return nil, err}req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))if err != nil {return nil, err}req.Header.Set("Authorization", "Bearer "+c.token)req.Header.Set("Token", c.token)req.Header.Set("Content-Type", "application/json")resp, err := c.client.Do(req)if err != nil {return nil, err}defer resp.Body.Close()var bookmarkResp BookmarkListResponseif err := json.NewDecoder(resp.Body).Decode(&bookmarkResp); err != nil {return nil, err}if bookmarkResp.Code != 0 {return nil, fmt.Errorf("get bookmarks failed: %s", bookmarkResp.Msg)}return bookmarkResp.Data.List, nil
}func (c *Client) CreateGroup(title string) (*Group, error) {url := fmt.Sprintf("%s/api/panel/itemIconGroup/edit", c.baseURL)fmt.Println(url)data := map[string]interface{}{"title": title,"cardStyle": map[string]interface{}{"style":                   0,"textColor":               "#ffffff","textInfoHideDescription": false,"textIconHideTitle":       false,},}jsonData, err := json.Marshal(data)if err != nil {return nil, err}req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))if err != nil {return nil, err}req.Header.Set("Authorization", "Bearer "+c.token)req.Header.Set("Token", c.token)req.Header.Set("Content-Type", "application/json")fmt.Println(req)resp, err := c.client.Do(req)if err != nil {return nil, err}defer resp.Body.Close()var groupResp struct {Code int    `json:"code"`Data Group  `json:"data"`Msg  string `json:"msg"`}if err := json.NewDecoder(resp.Body).Decode(&groupResp); err != nil {return nil, err}if groupResp.Code != 0 {return nil, fmt.Errorf("create group failed: %s", groupResp.Msg)}return &groupResp.Data, nil
}func (c *Client) CreateBookmark(groupID int, title, url, description string) (*Bookmark, error) {apiURL := fmt.Sprintf("%s/api/panel/itemIcon/edit", c.baseURL)data := map[string]interface{}{"icon": map[string]interface{}{"itemType":        1,"backgroundColor": "#2a2a2a6b",},"title":           title,"url":             url,"lanUrl":          url,"description":     description,"openMethod":      2,"cardType":        1,"itemIconGroupId": groupID,"backgroundColor": "#2a2a2a6b","expandParam":     map[string]interface{}{},}jsonData, err := json.Marshal(data)if err != nil {return nil, err}req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData))if err != nil {return nil, err}req.Header.Set("Authorization", "Bearer "+c.token)req.Header.Set("Token", c.token)req.Header.Set("Content-Type", "application/json")resp, err := c.client.Do(req)if err != nil {return nil, err}defer resp.Body.Close()var bookmarkResp struct {Code int      `json:"code"`Data Bookmark `json:"data"`Msg  string   `json:"msg"`}if err := json.NewDecoder(resp.Body).Decode(&bookmarkResp); err != nil {return nil, err}if bookmarkResp.Code != 0 {return nil, fmt.Errorf("create bookmark failed: %s", bookmarkResp.Msg)}return &bookmarkResp.Data, nil
}func (c *Client) GetAuthInfo() (*UserInfo, error) {url := fmt.Sprintf("%s/api/user/getAuthInfo", c.baseURL)req, err := http.NewRequest("GET", url, nil)if err != nil {return nil, err}req.Header.Set("Authorization", "Bearer "+c.token)req.Header.Set("Token", c.token)req.Header.Set("Content-Type", "application/json")resp, err := c.client.Do(req)if err != nil {return nil, err}defer resp.Body.Close()var authResp AuthInfoResponseif err := json.NewDecoder(resp.Body).Decode(&authResp); err != nil {return nil, err}if authResp.Code != 0 {return nil, fmt.Errorf("get auth info failed: %s", authResp.Msg)}return &authResp.Data.User, nil
}

4.2 同步命令实现

4.2.1 Pull命令实现
// cmd/sp.govar pullCmd = &cobra.Command{Use:   "pull",Short: "pull data from sun-panel",Long:  `pull data from sun-panel`,Run: func(cmd *cobra.Command, args []string) {db, err := utils.GetGormDB()if err != nil {log.Fatal("Failed to get db", zap.Error(err))}client := GetClient()groups, err := client.GetGroups()if err != nil {log.Fatal("Failed to get groups", zap.Error(err))}log.Info("groups", zap.Any("groups", groups))for _, group := range groups {// 查询数据库中是否存在该分组var category models.Categorydb.Model(&models.Category{}).Where("name = ?", group.Title).First(&category)targetGroupId := category.IDif category.ID == 0 {// 保存到数据库category = models.Category{Name:        group.Title,Description: "由同步服务创建",}db.Create(&category)targetGroupId = category.ID}bookmarks, err := client.GetBookmarksByGroup(group.ID)if err != nil {log.Fatal("Failed to get bookmarks", zap.Error(err))}log.Info("bookmarks", zap.Any("bookmarks", bookmarks))for _, bookmark := range bookmarks {// 根据分组和标题查询数据库中是否存在该书签var existBookmark models.Bookmarkdb.Model(&models.Bookmark{}).Where("category_id = ? AND title = ?", targetGroupId, bookmark.Title).First(&existBookmark)if existBookmark.ID == 0 {// 保存到数据库saveBookmark := models.Bookmark{Title:       bookmark.Title,URL:         bookmark.URL,Description: bookmark.Description,CategoryID:  targetGroupId,}db.Create(&saveBookmark)}}}log.Info("pull data from sun-panel success")fmt.Println("pull data from sun-panel success")},
}
4.2.2 Push命令实现
// cmd/sp.govar pushCmd = &cobra.Command{Use:   "push",Short: "push data to sun-panel",Long:  `push data to sun-panel`,Run: func(cmd *cobra.Command, args []string) {db, err := utils.GetGormDB()if err != nil {log.Fatal("Failed to get db", zap.Error(err))}var categories []models.Categorydb.Find(&categories)client := GetClient()groups, err := client.GetGroups()if err != nil {log.Fatal("Failed to get groups", zap.Error(err))}log.Info("groups", zap.Any("groups", groups))for _, category := range categories {// 查询数据库中所有书签var bookmarks []models.Bookmarkdb.Model(&models.Bookmark{}).Where("category_id = ?", category.ID).Find(&bookmarks)// 查询group中是否存在该分组var existGroup sunpanel.Groupfor _, group := range groups {if group.Title == category.Name {existGroup = group}}if existGroup.ID == 0 {// 创建分组existGroup, err := client.CreateGroup(category.Name)if err != nil {log.Fatal("Failed to create group", zap.Error(err))}for _, bookmark := range bookmarks {client.CreateBookmark(existGroup.ID, bookmark.Title, bookmark.URL, bookmark.Description)}} else {// 查询该分组下所有书签spbookmarks, err := client.GetBookmarksByGroup(existGroup.ID)if err != nil {log.Fatal("Failed to get bookmarks", zap.Error(err))}for _, bookmark := range bookmarks {// 查询spbookmarks中是否存在该书签var existBookmark sunpanel.Bookmarkfor _, spbookmark := range spbookmarks {if spbookmark.Title == bookmark.Title {existBookmark = spbookmark}}if existBookmark.ID == 0 {client.CreateBookmark(existGroup.ID, bookmark.Title, bookmark.URL, bookmark.Description)}}}}log.Info("push data to sun-panel success")fmt.Println("push data to sun-panel success")},
}
4.2.3 Sync命令实现(双向同步)
// cmd/sp.govar syncCmd = &cobra.Command{Use:   "sync",Short: "sync data between sun-panel and local",Long:  `sync data between sun-panel and local`,Run: func(cmd *cobra.Command, args []string) {// 获取数据库连接db, err := utils.GetGormDB()if err != nil {log.Fatal("Failed to get db", zap.Error(err))}// 获取客户端client := GetClient()// 1. 从远程获取所有分组groups, err := client.GetGroups()if err != nil {log.Fatal("Failed to get groups", zap.Error(err))}log.Info("Remote groups", zap.Any("groups", groups))// 2. 获取本地所有分类var localCategories []models.Categorydb.Find(&localCategories)log.Info("Local categories", zap.Any("categories", localCategories))// 3. 同步分组/分类for _, group := range groups {// 检查本地是否存在该分组var category models.Categorydb.Model(&models.Category{}).Where("name = ?", group.Title).First(&category)if category.ID == 0 {// 本地不存在,创建新分类category = models.Category{Name:        group.Title,Description: "由同步服务创建",}db.Create(&category)}// 获取远程书签remoteBookmarks, err := client.GetBookmarksByGroup(group.ID)if err != nil {log.Fatal("Failed to get remote bookmarks", zap.Error(err))}// 获取本地书签var localBookmarks []models.Bookmarkdb.Model(&models.Bookmark{}).Where("category_id = ?", category.ID).Find(&localBookmarks)// 同步书签for _, remoteBookmark := range remoteBookmarks {var existBookmark models.Bookmarkdb.Model(&models.Bookmark{}).Where("category_id = ? AND title = ?", category.ID, remoteBookmark.Title).First(&existBookmark)if existBookmark.ID == 0 {// 本地不存在,创建新书签saveBookmark := models.Bookmark{Title:       remoteBookmark.Title,URL:         remoteBookmark.URL,Description: remoteBookmark.Description,CategoryID:  category.ID,}db.Create(&saveBookmark)}}}// 4. 处理本地特有的分类for _, category := range localCategories {// 检查远程是否存在该分组var existGroup sunpanel.Groupfor _, group := range groups {if group.Title == category.Name {existGroup = groupbreak}}if existGroup.ID == 0 {// 远程不存在,创建新分组newGroup, err := client.CreateGroup(category.Name)if err != nil {log.Fatal("Failed to create remote group", zap.Error(err))}existGroup = *newGroup}// 获取本地书签var localBookmarks []models.Bookmarkdb.Model(&models.Bookmark{}).Where("category_id = ?", category.ID).Find(&localBookmarks)// 获取远程书签remoteBookmarks, err := client.GetBookmarksByGroup(existGroup.ID)if err != nil {log.Fatal("Failed to get remote bookmarks", zap.Error(err))}// 同步书签到远程for _, localBookmark := range localBookmarks {var existRemoteBookmark sunpanel.Bookmarkfor _, remoteBookmark := range remoteBookmarks {if remoteBookmark.Title == localBookmark.Title {existRemoteBookmark = remoteBookmarkbreak}}if existRemoteBookmark.ID == 0 {// 远程不存在,创建新书签_, err := client.CreateBookmark(existGroup.ID, localBookmark.Title, localBookmark.URL, localBookmark.Description)if err != nil {log.Fatal("Failed to create remote bookmark", zap.Error(err))}}}}log.Info("Sync completed successfully")fmt.Println("Sync completed successfully")},
}

4.3 用户配置管理

首次使用或者配置有误需要更新sun-panel配置信息:

func HandleSpConfigInput() {config := common.AppConfigModelif config == nil {log.Fatal("Failed to get config")}log.Info("sp config called", zap.Any("config", config))urlPromot := promptui.Prompt{Label:  "请输入sun-panel url",Stdout: os.Stderr,}url, err := urlPromot.Run()if err != nil {log.Fatal("Failed to get url", zap.Error(err))}// userNameuserNamePromot := promptui.Prompt{Label:  "请输入sun-panel用户名",Stdout: os.Stderr,}userName, err := userNamePromot.Run()if err != nil {log.Fatal("Failed to get userName", zap.Error(err))}// passwordpasswordPromot := promptui.Prompt{Label:  "请输入sun-panel密码",Mask:   '*',Stdout: os.Stderr,}password, err := passwordPromot.Run()if err != nil {log.Fatal("Failed to get password", zap.Error(err))}//调用client := sunpanel.NewClient(url)token, err := client.Login(userName, password)if err != nil {log.Fatal("Failed to login", zap.Error(err))}config.SunPanel.URL = urlconfig.SunPanel.Token = tokencommon.AppConfigModel = config// 写入配置utils.ConfigInstance.SaveConfig(config)
}

5. 使用演示

5.1 配置Sun-Panel连接 🎥

配置sun-panel的url和token,不会保存用户名和密码,仅获取token时需要。token失效时间在sun-panel侧配置。

$ ./aibookmark config sp
请输入sun-panel url: http://localhost:9000
请输入sun-panel用户名: admin
请输入sun-panel密码: ******

5.2 从Sun-Panel拉取数据

$ ./aibookmark sp pull
pull data from sun-panel success

5.3 推送数据到Sun-Panel

$ ./aibookmark sp push
push data to sun-panel success

5.4 双向同步

$ ./aibookmark sp sync
Sync completed successfully

6. 总结 📝

本文实现了与Sun-Panel的书签同步功能,主要完成了:

  1. Sun-Panel客户端封装,支持所有必要的API操作
  2. 三种同步模式:Pull、Push和Sync
  3. 用户友好的配置流程,简化首次使用设置
  4. 健壮的错误处理,确保同步过程可靠

书签管理不仅仅是收藏,更是知识的流动与共享。 通过实现强大的同步功能,我们让书签真正活起来,在不同设备、不同平台间自由流动,为用户创造无缝的知识管理体验。


往期系列

  • Ai书签管理工具开发全记录(一):项目总览与技术蓝图
  • Ai书签管理工具开发全记录(二):项目基础框架搭建
  • AI书签管理工具开发全记录(三):配置及数据系统设计
  • AI书签管理工具开发全记录(四):日志系统设计与实现
  • AI书签管理工具开发全记录(五):后端服务搭建与API实现
  • AI书签管理工具开发全记录(六):前端管理基础框框搭建 Vue3+Element Plus
  • AI书签管理工具开发全记录(七):页面编写与接口对接
  • AI书签管理工具开发全记录(八):Ai创建书签功能实现
  • AI书签管理工具开发全记录(九):用户端页面集成与展示
  • AI书签管理工具开发全记录(十):命令行中结合ai高效添加书签
  • AI书签管理工具开发全记录(十一):MCP集成
  • AI书签管理工具开发全记录(十二):MCP集成查询
  • AI书签管理工具开发全记录(十三):TUI基本框架搭建
  • AI书签管理工具开发全记录(十四):TUI基本界面完善
  • AI书签管理工具开发全记录(十五):TUI基本逻辑实现与数据展示
  • # AI书签管理工具开发全记录(十六):Sun-Panel接口分析
http://www.dtcms.com/wzjs/457695.html

相关文章:

  • 网站建设手机端管网电子商务营销模式有哪些
  • 小城镇建设的网站百度官方人工客服电话
  • 阳谷网站建设网络推广服务协议
  • 海外网站seo推广公司属于什么公司
  • 单县网站建设seo教程网
  • 网上ui设计培训seo中文意思是
  • 建设工程招标专业网站淘宝交易指数换算工具
  • 广西城乡建设网站seo关键词布局技巧
  • 西班牙外贸网站合肥百度快速排名提升
  • 网站空间怎么建站每日新闻摘抄10一30字
  • 泰安网络直销公司哪里搜索引擎优化好
  • 上海新闻网下载长沙seo关键词
  • 微网站 方案南宁网站关键词推广
  • 虚拟网站建设指导河北seo网络优化培训
  • 做网站好一点的软件南宁seo推广优化
  • 做a动态网站重庆seo整站优化外包服务
  • 网页主图模板seo网络推广机构
  • 百度做网站的费用软文街
  • 专业网站制作设推荐一个seo优化软件
  • 余姚做网站设计的百度免费推广怎么做
  • 郑州加盟网站建设qq推广链接
  • 自动生成网站武汉久都seo
  • 邢台网页设计seo快速优化技术
  • 行业前10的网站建设公司seo视频教程我要自学网
  • 教育网站开发需求专业北京网站建设公司
  • 帝国网站管理系统安装教程360收录提交入口
  • wordpress 菜单怎么使用上海百度移动关键词排名优化
  • 东莞网站搭建如何进行网站推广?网站推广的基本手段有哪些
  • html网站开发图片素材网站开发软件
  • 网站做自适应百度指数1000搜索量有多少