Init v3.2

This commit is contained in:
JiXieShi
2024-03-21 15:25:12 +08:00
commit 6e70a1d4df
49 changed files with 2115 additions and 0 deletions

238
models/articles.go Normal file
View File

@@ -0,0 +1,238 @@
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) {
article, _, err := readMarkdown(path)
if err != nil {
return article, err
}
return article, nil
}
func ReadArticleDetail(path string) (ArticleDetail, error) {
_, articleDetail, err := readMarkdown(path)
if err != nil {
return articleDetail, err
}
return articleDetail, nil
}
func readMarkdown(path string) (Article, ArticleDetail, error) {
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)
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] }

69
models/category.go Normal file
View File

@@ -0,0 +1,69 @@
package models
import (
"blog/config"
"sort"
"strings"
)
type Category struct {
Name string
Quantity int
Articles Articles
}
type Categories []Category
func GetCategoryName(path string) string {
var categoryName string
newPath := strings.Replace(path, config.Cfg.DocumentContentDir+"/", "", 1)
if strings.Index(newPath, "/") == -1 { //文件在根目录下(content/)没有分类名称
categoryName = "未分类"
} else {
categoryName = strings.Split(newPath, "/")[0]
}
return categoryName
}
func GroupByCategory(articles *Articles, articleQuantity int) Categories {
var categories Categories
categoryMap := make(map[string]Articles)
for _, article := range *articles {
_, existedCategory := categoryMap[article.Category]
if existedCategory {
categoryMap[article.Category] = append(categoryMap[article.Category], article)
} else {
categoryMap[article.Category] = Articles{article}
}
}
for categoryName, articles := range categoryMap {
articleLen := len(articles)
var articleList Articles
if articleQuantity <= 0 {
articleList = articles
} else {
if articleQuantity > articleLen {
articleList = articles[0:articleLen]
} else {
articleList = articles[0:articleQuantity]
}
}
categories = append(categories, Category{
Name: categoryName,
Quantity: articleLen,
Articles: articleList,
})
}
sort.Sort(categories)
return categories
}
func (c Categories) Len() int { return len(c) }
func (c Categories) Less(i, j int) bool { return c[i].Quantity > c[j].Quantity }
func (c Categories) Swap(i, j int) { c[i], c[j] = c[j], c[i] }

50
models/common.go Normal file
View File

@@ -0,0 +1,50 @@
package models
import (
"blog/config"
"sync"
)
var Navigation Navs
var ArticleList Articles
var ArticleShortUrlMap map[string]string //用来保证文章 shortUrl 唯一和快速定位文章
var Template HtmlTemplate
func CompiledContent() {
config.Initial() //克隆或者更新文档库
//下面是对内容的生成
wg := sync.WaitGroup{}
var err error
//导航
wg.Add(1)
go func() {
Navigation, err = initExtraNav(config.Cfg.DocumentExtraNavDir)
if err != nil {
panic(err)
}
wg.Done()
}()
//加载html模板
wg.Add(1)
go func() {
Template, err = initHtmlTemplate(config.Cfg.ThemesDir)
if err != nil {
panic(err)
}
wg.Done()
}()
//文章
wg.Add(1)
go func() {
ArticleList, ArticleShortUrlMap, err = initArticlesAndImages(config.Cfg.DocumentContentDir)
if err != nil {
panic(err)
}
wg.Done()
}()
wg.Wait()
//启用并发比之前节约4倍左右的时间
return
}

31
models/extra_nav.go Normal file
View File

@@ -0,0 +1,31 @@
package models
import (
"sort"
"strings"
)
type Nav struct {
Title string
Path string
}
type Navs []Nav
func initExtraNav(dir string) (Navs, error) {
var navigation Navs
var extraNav Articles
extraNav, err := RecursiveReadArticles(dir)
if err != nil {
return navigation, err
}
sort.Sort(extraNav)
for _, article := range extraNav {
title := strings.Title(strings.ToLower(article.Title))
navigation = append(navigation, Nav{Title: title, Path: article.Path})
}
return navigation, nil
}

91
models/html_template.go Normal file
View File

@@ -0,0 +1,91 @@
package models
import (
"blog/config"
"fmt"
"html/template"
"io"
)
type TemplatePointer struct {
*template.Template
}
type HtmlTemplate struct {
Article TemplatePointer
Categories TemplatePointer
Dashboard TemplatePointer
ExtraNav TemplatePointer
Index TemplatePointer
}
func (t TemplatePointer) WriteData(w io.Writer, data interface{}) {
err := t.Execute(w, data)
if err != nil {
if _, e := w.Write([]byte(err.Error())); e != nil {
fmt.Println(e)
}
}
}
func (t TemplatePointer) WriteError(w io.Writer, err error) {
if _, e := w.Write([]byte(err.Error())); e != nil {
fmt.Println(e)
}
}
func BuildViewData(title string, data interface{}) map[string]interface{} {
return map[string]interface{}{
"Title": title,
"Data": data,
"Config": config.Cfg,
"Navs": Navigation,
}
}
func initHtmlTemplate(viewDir string) (HtmlTemplate, error) {
var htmlTemplate HtmlTemplate
tp, err := readHtmlTemplate(
[]string{"index", "extraNav", "dashboard", "categories", "article"},
viewDir)
if err != nil {
return htmlTemplate, err
}
htmlTemplate.Index = tp[0]
htmlTemplate.ExtraNav = tp[1]
htmlTemplate.Dashboard = tp[2]
htmlTemplate.Categories = tp[3]
htmlTemplate.Article = tp[4]
return htmlTemplate, nil
}
func SpreadDigit(n int) []int {
var r []int
for i := 1; i <= n; i++ {
r = append(r, i)
}
return r
}
func readHtmlTemplate(htmlFileName []string, viewDir string) ([]TemplatePointer, error) {
var htmlTemplate []TemplatePointer
head := viewDir + "/layouts/head.html"
footer := viewDir + "/layouts/footer.html"
for _, name := range htmlFileName {
tp, err := template.New(name+".html").
Funcs(template.FuncMap{"SpreadDigit": SpreadDigit}).
ParseFiles(viewDir+"/"+name+".html", head, footer)
if err != nil {
return htmlTemplate, err
}
htmlTemplate = append(htmlTemplate, TemplatePointer{tp})
}
return htmlTemplate, nil
}

48
models/pagination.go Normal file
View File

@@ -0,0 +1,48 @@
package models
import (
"math"
)
type PageResult struct {
List Articles `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
TotalPage int
}
func Pagination(articles *Articles, page int, pageSize int) PageResult {
articleLen := len(*articles)
totalPage := int(math.Floor(float64(articleLen / pageSize)))
if (articleLen % pageSize) != 0 {
totalPage++
}
result := PageResult{
Total: articleLen,
Page: page,
PageSize: pageSize,
TotalPage: totalPage,
}
if page < 1 {
result.Page = 1
}
if page > result.TotalPage {
result.Page = result.TotalPage
}
if articleLen <= result.PageSize {
result.List = (*articles)[0:articleLen]
} else {
startNum := (result.Page - 1) * result.PageSize
endNum := startNum + result.PageSize
if endNum > articleLen {
endNum = articleLen
}
result.List = (*articles)[startNum:endNum]
}
return result
}