From dcb18e61788d47091e0ffce31eacd212acff0899 Mon Sep 17 00:00:00 2001 From: d100972 Date: Fri, 21 Jun 2024 15:30:04 +0800 Subject: [PATCH] fix: v1.0.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 是什么,包含什么改变?: 1. 新增 enid 字段,该字段是 dedao 每本书的唯一 id,既可以用来请求到书的详情页,也可作为每一个 feed 的唯一 id; 2. atom 文件的创建时间、更新时间加以区别; 3. 优化函数注释以及打印日志; 4. main_test.go 增加单元测试函数; 如何解决的问题?: 无 本次提交影响范围: all --- go.mod | 3 ++ main.go | 28 ++++++++++++--- main_test.go | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 main_test.go diff --git a/go.mod b/go.mod index 9998e3b..5c87fc6 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.20 require ( github.com/gin-gonic/gin v1.10.0 github.com/gorilla/feeds v1.2.0 + github.com/stretchr/testify v1.9.0 ) require ( @@ -12,6 +13,7 @@ require ( github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -25,6 +27,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect golang.org/x/arch v0.8.0 // indirect diff --git a/main.go b/main.go index 5581b02..c4ec45d 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,7 @@ type Book struct { PublishTime string `json:"publish_time"` Uptime string `json:"uptime"` OtherShareSummary string `json:"other_share_summary"` - Enid string `json:"enid"` + Enid string `json:"enid"` // dedao 每本书的唯一标识 } type Response struct { @@ -43,13 +43,21 @@ func initLogger() *log.Logger { } var logger = initLogger() +var initialCreatedTime time.Time +func init() { + // 初始化时设置创建时间 + initialCreatedTime = time.Now() +} + +// fetchBooks 从得到获取电子书列表 func fetchBooks() ([]Book, error) { url := "https://m.igetget.com/native/api/ebook/getBookList" payload := `{"count": 50, "max_id": 0, "sort": "time", "since_id": 0}` req, err := http.NewRequest("POST", url, strings.NewReader(payload)) if err != nil { + logger.Println("Error creating request:", err) return nil, err } @@ -59,6 +67,7 @@ func fetchBooks() ([]Book, error) { client := &http.Client{} resp, err := client.Do(req) if err != nil { + logger.Println("Error sending request:", err) return nil, err } defer resp.Body.Close() @@ -67,23 +76,32 @@ func fetchBooks() ([]Book, error) { C Response `json:"c"` } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + logger.Println("Error decoding response:", err) return nil, err } return result.C.List, nil } +// generateAtom 生成 Atom 订阅源 func generateAtom(books []Book) (string, error) { now := time.Now() feed := &feeds.Feed{ Title: "得到最新电子书 Atom 订阅源", Link: &feeds.Link{Href: "https://m.igetget.com/native/ebook/#/ebook/newBookList"}, Description: "得到最新电子书更新", - Created: now, + Created: initialCreatedTime, + Updated: now, } // book detail url: https://www.dedao.cn/ebook/detail?id=enid for _, book := range books { + createdTime, err := time.Parse("2006-01-02 15:04:05", book.Uptime) + if err != nil { + logger.Println("Error parsing time:", err) + continue + } + item := &feeds.Item{ Title: book.Title, Link: &feeds.Link{Href: fmt.Sprintf("https://www.dedao.cn/ebook/detail?id=%s", book.Enid)}, @@ -96,8 +114,8 @@ func generateAtom(books []Book) (string, error) { book.PublishTime, ), Author: &feeds.Author{Name: book.Author}, - Created: now, - Id: book.Cover, + Created: createdTime, + Id: book.Enid, Description: book.OtherShareSummary, } feed.Items = append(feed.Items, item) @@ -112,6 +130,7 @@ func generateAtom(books []Book) (string, error) { return atom, nil } +// saveAtomToFile 将 Atom 订阅源保存到文件 func saveAtomToFile(atom string) error { file, err := os.Create("dedao.atom") if err != nil { @@ -129,6 +148,7 @@ func saveAtomToFile(atom string) error { return nil } +// updateAtomFile 更新 Atom 订阅源 func updateAtomFile() { defer func() { if r := recover(); r != nil { diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..2bf1156 --- /dev/null +++ b/main_test.go @@ -0,0 +1,98 @@ +package main + +import ( + "net/http" + "net/http/httptest" + "os" + "testing" + "time" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" +) + +func TestFetchBooks(t *testing.T) { + books, err := fetchBooks() + if err != nil { + t.Fatalf("fetchBooks failed: %v", err) + } + + if len(books) != 50 { + t.Fatalf("expected 50 books, got %d", len(books)) + } +} + +func TestGenerateAtom(t *testing.T) { + books := []Book{ + { + Author: "Author1", + Cover: "http://example.com/cover1.jpg", + Title: "Title1", + AuthorInfo: "Author Info 1", + BookIntro: "Book Intro 1", + PublishTime: "2023-01-01", + Uptime: "2023-01-01 00:00:00", + OtherShareSummary: "Summary 1", + Enid: "enid1", + }, + } + + atom, err := generateAtom(books) + if err != nil { + t.Fatalf("generateAtom failed: %v", err) + } + + assert.Contains(t, atom, "Title1") + assert.Contains(t, atom, "http://example.com/cover1.jpg") +} + +func TestSaveAtomToFile(t *testing.T) { + atom := "Test Feed" + err := saveAtomToFile(atom) + if err != nil { + t.Fatalf("saveAtomToFile failed: %v", err) + } + + data, err := os.ReadFile("dedao.atom") + if err != nil { + t.Fatalf("ReadFile failed: %v", err) + } + + assert.Equal(t, atom, string(data)) +} + +func TestUpdateAtomFile(t *testing.T) { + go updateAtomFile() + + time.Sleep(5 * time.Second) + + data, err := os.ReadFile("dedao.atom") + if err != nil { + t.Fatalf("ReadFile failed: %v", err) + } + + assert.Contains(t, string(data), "") +} + +func TestMainRoute(t *testing.T) { + gin.SetMode(gin.TestMode) + r := gin.Default() + + r.GET("/feeds/dedao.atom", func(c *gin.Context) { + data, err := os.ReadFile("dedao.atom") + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Read Atom file failed"}) + return + } + + c.Header("Content-Type", "application/atom+xml; charset=utf-8") + c.Data(http.StatusOK, "application/atom+xml; charset=utf-8", data) + }) + + req, _ := http.NewRequest("GET", "/feeds/dedao.atom", nil) + w := httptest.NewRecorder() + r.ServeHTTP(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + assert.Contains(t, w.Body.String(), "") +}