Blog/models/articles.go

241 lines
5.6 KiB
Go
Raw Normal View History

2024-03-21 07:25:12 +00:00
package models
import (
"blog/config"
"blog/utils"
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"sort"
"strings"
"time"
)
type Time time.Time
type Article struct {
Title string `json:"title"`
Date Time `json:"date"`
Description string `json:"description"`
Tags []string `json:"tags"`
Author string `json:"author"`
MusicId string `json:"musicId"`
Path string
ShortUrl string
Category string
}
type Articles []Article
type ArticleDetail struct {
Article
Body string
}
func initArticlesAndImages(dir string) (Articles, map[string]string, error) {
var articles Articles
shortUrlMap := make(map[string]string)
articles, err := RecursiveReadArticles(dir)
if err != nil {
return articles, shortUrlMap, err
}
sort.Sort(articles)
for i := len(articles) - 1; i >= 0; i-- {
//这里必须使用倒序的方式生成 shortUrl,因为如果有相同的文章标题,
// 倒序会将最老的文章优先生成shortUrl保证和之前的 shortUrl一样
article := articles[i]
keyword := utils.GenerateShortUrl(article.Title, func(url, keyword string) bool {
//保证 keyword 唯一
_, ok := shortUrlMap[keyword]
return !ok
})
articles[i].ShortUrl = keyword
shortUrlMap[keyword] = article.Path
}
return articles, shortUrlMap, nil
}
func ArticleSearch(articles *Articles, search string, category string, tag string) Articles {
var articleList Articles
for _, article := range *articles {
pass := true
if search != "" && strings.Index(article.Title, search) == -1 {
pass = false
}
if category != "" && strings.Index(article.Category, category) == -1 {
pass = false
}
if tag != "" {
pass = false
for _, tagx := range article.Tags {
if strings.Index(tagx, tag) != -1 {
pass = true
break
}
}
}
if pass {
articleList = append(articleList, article)
}
}
return articleList
}
func RecursiveReadArticles(dir string) (Articles, error) {
var articles Articles
dirInfo, err := os.Stat(dir)
if err != nil {
return articles, err
}
if !dirInfo.IsDir() {
return articles, errors.New("目标不是一个目录")
}
fileOrDir, err := ioutil.ReadDir(dir)
if err != nil {
return articles, err
}
for _, fileInfo := range fileOrDir {
name := fileInfo.Name()
path := dir + "/" + name
upperName := strings.ToUpper(name)
if fileInfo.IsDir() {
subArticles, err := RecursiveReadArticles(path)
if err != nil {
return articles, err
}
articles = append(articles, subArticles...)
} else if strings.HasSuffix(upperName, ".MD") {
article, err := ReadArticle(path)
if err != nil {
return articles, err
}
articles = append(articles, article)
} else if strings.HasSuffix(upperName, ".PNG") ||
strings.HasSuffix(upperName, ".GIF") ||
strings.HasSuffix(upperName, ".JPG") {
dst := config.Cfg.CurrentDir + "/images/" + name
fmt.Println(utils.IsFile(dst))
if !utils.IsFile(dst) {
_, _ = utils.CopyFile(path, dst)
}
}
}
return articles, nil
}
func ReadArticle(path string) (Article, error) {
2024-03-28 11:45:25 +00:00
article, _, err := readMarkdown(path, "")
2024-03-21 07:25:12 +00:00
if err != nil {
return article, err
}
return article, nil
}
2024-03-28 11:45:25 +00:00
func ReadArticleDetail(path, key string) (ArticleDetail, error) {
_, articleDetail, err := readMarkdown(path, key)
2024-03-21 07:25:12 +00:00
if err != nil {
return articleDetail, err
}
return articleDetail, nil
}
2024-03-28 11:45:25 +00:00
func readMarkdown(path, key string) (Article, ArticleDetail, error) {
2024-03-21 07:25:12 +00:00
var article Article
var articleDetail ArticleDetail
mdFile, err := os.Stat(path)
if err != nil {
return article, articleDetail, err
}
if mdFile.IsDir() {
return article, articleDetail, errors.New("this path is Dir")
}
markdown, err := ioutil.ReadFile(path)
if err != nil {
return article, articleDetail, err
}
markdown = bytes.TrimSpace(markdown)
2024-03-28 11:45:25 +00:00
if key != "" {
article.ShortUrl = key
}
2024-03-21 07:25:12 +00:00
article.Path = path
article.Category = GetCategoryName(path)
article.Title = strings.TrimSuffix(strings.ToUpper(mdFile.Name()), ".MD")
article.Date = Time(mdFile.ModTime())
if !bytes.HasPrefix(markdown, []byte("```json")) {
article.Description = cropDesc(markdown)
articleDetail.Article = article
articleDetail.Body = string(markdown)
return article, articleDetail, nil
}
markdown = bytes.Replace(markdown, []byte("```json"), []byte(""), 1)
markdownArrInfo := bytes.SplitN(markdown, []byte("```"), 2)
article.Description = cropDesc(markdownArrInfo[1])
if err := json.Unmarshal(bytes.TrimSpace(markdownArrInfo[0]), &article); err != nil {
article.Title = "文章[" + article.Title + "]解析 JSON 出错,请检查。"
article.Description = err.Error()
return article, articleDetail, nil
}
article.Path = path
article.Title = strings.ToUpper(article.Title)
articleDetail.Article = article
articleDetail.Body = string(markdownArrInfo[1])
return article, articleDetail, nil
}
func cropDesc(c []byte) string {
content := []rune(string(c))
contentLen := len(content)
if contentLen <= config.Cfg.DescriptionLen {
return string(content[0:contentLen])
}
return string(content[0:config.Cfg.DescriptionLen])
}
func (t *Time) UnmarshalJSON(b []byte) error {
date, err := time.ParseInLocation(`"`+config.Cfg.TimeLayout+`"`, string(b), time.Local)
if err != nil {
return nil
}
*t = Time(date)
return nil
}
func (t Time) MarshalJSON() ([]byte, error) {
return []byte(t.Format(`"` + config.Cfg.TimeLayout + `"`)), nil
}
func (t Time) Format(layout string) string {
return time.Time(t).Format(layout)
}
func (a Articles) Len() int { return len(a) }
func (a Articles) Less(i, j int) bool { return time.Time(a[i].Date).After(time.Time(a[j].Date)) }
func (a Articles) Swap(i, j int) { a[i], a[j] = a[j], a[i] }