使用常量标记状态和Redis的key,增加了文件管理功能,修改tinymce页面增加文件上传功能
This commit is contained in:
		
							parent
							
								
									e1d0017257
								
							
						
					
					
						commit
						a2d868d354
					
				|  | @ -1,6 +1,7 @@ | |||
| package async | ||||
| 
 | ||||
| import ( | ||||
| 	"blog/internal/consts" | ||||
| 	"blog/internal/model/blog" | ||||
| 	"blog/third_party/database" | ||||
| 	"context" | ||||
|  | @ -27,7 +28,7 @@ func init() { | |||
| 			database.GormTemplate.Table("blog_articles").Where("id = ?", id).UpdateColumn("publish_time", time) | ||||
| 			database.GormTemplate.Table("blog_articles").Where("id = ?", id).First(article) | ||||
| 
 | ||||
| 			database.RedisTemplate.ZAdd(ctx, "blog:article:latest", redis.Z{Score: float64(article.PublishTime), Member: article}) | ||||
| 			database.RedisTemplate.ZAdd(ctx, consts.REDIS_BLOG_ARTICLE_LATEST, redis.Z{Score: float64(article.PublishTime), Member: article}) | ||||
| 		} | ||||
| 	}() | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| package async | ||||
| 
 | ||||
| import ( | ||||
| 	"blog/internal/consts" | ||||
| 	"blog/internal/model/blog" | ||||
| 	"blog/third_party/database" | ||||
| 	"context" | ||||
|  | @ -22,7 +23,7 @@ func init() { | |||
| 
 | ||||
| 			var diary blog.BlogDiary | ||||
| 			database.GormTemplate.Table("blog_diaries").Where("id = ?", id).First(&diary) | ||||
| 			database.RedisTemplate.ZAdd(ctx, "blog:diary:latest", redis.Z{Score: float64(diary.PublishTime), Member: diary}) | ||||
| 			database.RedisTemplate.ZAdd(ctx, consts.REDIS_BLOG_DIARY_LATEST, redis.Z{Score: float64(diary.PublishTime), Member: diary}) | ||||
| 		} | ||||
| 	}() | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| package async | ||||
| 
 | ||||
| import ( | ||||
| 	"blog/internal/consts" | ||||
| 	"blog/third_party/database" | ||||
| 	"context" | ||||
| 	"log" | ||||
|  | @ -30,7 +31,7 @@ func init() { | |||
| 			record.Id = uuid.NewString() | ||||
| 			database.GormTemplate.Table("blog_view_record").Create(&record) | ||||
| 			ctx := context.Background() | ||||
| 			database.RedisTemplate.HIncrBy(ctx, "blog:view:record", relId, 1) | ||||
| 			database.RedisTemplate.HIncrBy(ctx, consts.REDIS_BLOG_VIEW_RECORD, relId, 1) | ||||
| 
 | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,8 @@ | |||
| package consts | ||||
| 
 | ||||
| //文章状态常量
 | ||||
| const ARTICLE_STATE_PUBLISH string = "publish" | ||||
| const ARTICLE_STATE_DRAFT string = "draft" | ||||
| 
 | ||||
| const ARTICLE_CONTENT_TYPE_MARKDOWN string = "markdown" | ||||
| const ARTICLE_CONTENT_TYPE_TINYMCE string = "tinymce" | ||||
|  | @ -0,0 +1,6 @@ | |||
| package consts | ||||
| 
 | ||||
| //文本状态常量
 | ||||
| 
 | ||||
| const CONTENT_STATE_PUBLISH string = "publish" | ||||
| const CONTENT_STATE_DOWN string = "down" | ||||
|  | @ -0,0 +1,21 @@ | |||
| package consts | ||||
| 
 | ||||
| //Redis
 | ||||
| 
 | ||||
| //文章相关Key常量
 | ||||
| const REDIS_BLOG_ARTICLE string = "blog:article:" | ||||
| const REDIS_BLOG_ARTICLE_LATEST string = "blog:article:latest" | ||||
| 
 | ||||
| //日记相关Key常量
 | ||||
| const REDIS_BLOG_DIARY string = "blog:diary:" | ||||
| const REDIS_BLOG_DIARY_LATEST string = "blog:diary:latest" | ||||
| 
 | ||||
| //文件相关Key常量
 | ||||
| const REDIS_FILE string = "blog:file:" | ||||
| const REDIS_FILE_BYTES string = "blog:file:bytes" | ||||
| 
 | ||||
| //文本相关Key常量
 | ||||
| const REDIS_BLOG_CONTENT string = "blog:content:" | ||||
| 
 | ||||
| //点击量相关Key常量
 | ||||
| const REDIS_BLOG_VIEW_RECORD string = "blog:view:record" | ||||
|  | @ -2,6 +2,7 @@ package controller | |||
| 
 | ||||
| import ( | ||||
| 	"blog/internal/async" | ||||
| 	"blog/internal/consts" | ||||
| 	"blog/internal/model/AjaxResult" | ||||
| 	"blog/internal/model/blog" | ||||
| 	"blog/internal/service" | ||||
|  | @ -28,7 +29,7 @@ func (ctrl *ArticleController) Get() { | |||
| func (ctrl *ArticleController) GetLatest() { | ||||
| 	ctx := context.Background() | ||||
| 	var slices []blog.BlogArticle | ||||
| 	err := database.RedisTemplate.ZRevRange(ctx, "blog:article:latest", 0, 10).ScanSlice(&slices) | ||||
| 	err := database.RedisTemplate.ZRevRange(ctx, consts.REDIS_BLOG_ARTICLE_LATEST, 0, 10).ScanSlice(&slices) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		ctrl.Ctx.JSON(AjaxResult.Error("加载错误")) | ||||
|  | @ -38,7 +39,7 @@ func (ctrl *ArticleController) GetLatest() { | |||
| 	for i := range slices { | ||||
| 		article := &slices[i] | ||||
| 		id := article.Id | ||||
| 		viewRecord, _ := database.RedisTemplate.HGet(ctx, "blog:view:record", id).Int() | ||||
| 		viewRecord, _ := database.RedisTemplate.HGet(ctx, consts.REDIS_BLOG_VIEW_RECORD, id).Int() | ||||
| 		article.ViewRecord = viewRecord | ||||
| 	} | ||||
| 
 | ||||
|  | @ -58,7 +59,7 @@ func (ctrl *ArticleController) ViewArticle() { | |||
| 	content := service.ContentService.GetContentByCache(articleId) | ||||
| 	ctrl.Ctx.ViewData("article", article) | ||||
| 	ctrl.Ctx.ViewData("content", content) | ||||
| 	if article.ContentType == "markdown" { | ||||
| 	if article.ContentType == consts.ARTICLE_CONTENT_TYPE_MARKDOWN { | ||||
| 		ctrl.Ctx.View("blog/article/article_md.html") | ||||
| 		return | ||||
| 	} | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| package controller | ||||
| 
 | ||||
| import ( | ||||
| 	"blog/internal/consts" | ||||
| 	"blog/internal/model/AjaxResult" | ||||
| 	"blog/internal/model/blog" | ||||
| 	"blog/internal/service" | ||||
|  | @ -24,7 +25,7 @@ func (ctrl *DiaryController) Get() { | |||
| func (ctrl *DiaryController) GetLatest() { | ||||
| 	ctx := context.Background() | ||||
| 	var slices []blog.BlogDiary | ||||
| 	err := database.RedisTemplate.ZRevRange(ctx, "blog:diary:latest", 0, 5).ScanSlice(&slices) | ||||
| 	err := database.RedisTemplate.ZRevRange(ctx, consts.REDIS_BLOG_DIARY_LATEST, 0, 5).ScanSlice(&slices) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		ctrl.Ctx.JSON(AjaxResult.Error("加载错误")) | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ package controller | |||
| 
 | ||||
| import ( | ||||
| 	"blog/internal/service" | ||||
| 	"net/url" | ||||
| 
 | ||||
| 	"github.com/kataras/iris/v12" | ||||
| 	"github.com/kataras/iris/v12/mvc" | ||||
|  | @ -14,10 +15,13 @@ type FileController struct { | |||
| } | ||||
| 
 | ||||
| func (ctrl *FileController) ViewFile() { | ||||
| 	relId := ctrl.Ctx.Params().Get("id") | ||||
| 	bytes := service.FileService.GetFile(relId) | ||||
| 	ctrl.Ctx.Write(bytes) | ||||
| 
 | ||||
| 	id := ctrl.Ctx.Params().Get("id") | ||||
| 	file, err := service.FileService.GetFile(id) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	ctrl.Ctx.Header("Content-Disposition", "attachment;filename="+url.QueryEscape(file.FileName)) | ||||
| 	ctrl.Ctx.Write(file.Data) | ||||
| } | ||||
| 
 | ||||
| func (ctrl *FileController) BeforeActivation(activation mvc.BeforeActivation) { | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ package admin | |||
| 
 | ||||
| import ( | ||||
| 	"blog/internal/model/admin" | ||||
| 	"blog/internal/service" | ||||
| 	"blog/third_party/SessionUtil" | ||||
| 	"blog/third_party/database" | ||||
| 	"time" | ||||
| 
 | ||||
|  | @ -23,6 +25,18 @@ type uploadResponse struct { | |||
| 	Location string `json:"location"` | ||||
| } | ||||
| 
 | ||||
| func (ctrl *FileController) Get() { | ||||
| 	ctrl.Ctx.View("/admin/file/index.html") | ||||
| } | ||||
| 
 | ||||
| func (ctrl *FileController) GetList() { | ||||
| 	page := ctrl.Ctx.URLParamIntDefault("page", 0) | ||||
| 	itemsPerPage := ctrl.Ctx.URLParamIntDefault("itemsPerPage", 10) | ||||
| 	pages := service.FileService.PageSysFiles(page, itemsPerPage) | ||||
| 
 | ||||
| 	ctrl.Ctx.JSON(pages) | ||||
| } | ||||
| 
 | ||||
| func (ctrl *FileController) PostUpload() { | ||||
| 	file, fileHeader, err := ctrl.Ctx.FormFile("editormd-image-file") | ||||
| 	if err != nil { | ||||
|  | @ -31,13 +45,22 @@ func (ctrl *FileController) PostUpload() { | |||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	user := SessionUtil.GetUser(ctrl.Session) | ||||
| 	fileId := uuid.NewString() | ||||
| 	fileName := fileHeader.Filename | ||||
| 	fileSize := fileHeader.Size | ||||
| 	var bytes []byte = make([]byte, fileSize) | ||||
| 	file.Read(bytes) | ||||
| 
 | ||||
| 	sysFile := admin.SysFile{Id: fileId, FileName: fileName, FileSize: fileSize, CreateTime: time.Now().UnixMilli(), Data: bytes, State: "1", Del: 0} | ||||
| 	sysFile := admin.SysFile{ | ||||
| 		Id:         fileId, | ||||
| 		FileName:   fileName, | ||||
| 		FileSize:   fileSize, | ||||
| 		CreateBy:   user.Username, | ||||
| 		CreateTime: time.Now().UnixMilli(), | ||||
| 		Data:       bytes, State: "1", | ||||
| 		Del: 0, | ||||
| 	} | ||||
| 	err = database.GormTemplate.Transaction(func(tx *gorm.DB) error { | ||||
| 		err2 := tx.Table("common_files").Create(sysFile).Error | ||||
| 		return err2 | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ import "encoding/json" | |||
| type CommonFiles struct { | ||||
| 	Id         string `json:"id" gorm:"primary_key"` | ||||
| 	FileName   string `json:"fileName"` | ||||
| 	Data       []byte | ||||
| 	Data       []byte `json:"-"` | ||||
| 	Sort       int32  `json:"sort"` | ||||
| 	CreateBy   string `json:"-"` | ||||
| 	CreateTime int64  `json:"-"` | ||||
|  | @ -15,11 +15,11 @@ type CommonFiles struct { | |||
| 	Del        int    `json:"-"` | ||||
| } | ||||
| 
 | ||||
| func (article *CommonFiles) MarshalBinary() (data []byte, err error) { | ||||
| func (file *CommonFiles) MarshalBinary() (data []byte, err error) { | ||||
| 	// encoding.BinaryMarshaler
 | ||||
| 	return json.Marshal(article) | ||||
| 	return json.Marshal(file) | ||||
| } | ||||
| 
 | ||||
| func (article *CommonFiles) UnmarshalBinary(data []byte) (err error) { | ||||
| 	return json.Unmarshal(data, article) | ||||
| func (file *CommonFiles) UnmarshalBinary(data []byte) (err error) { | ||||
| 	return json.Unmarshal(data, file) | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| package service | ||||
| 
 | ||||
| import ( | ||||
| 	"blog/internal/consts" | ||||
| 	"blog/internal/model/admin" | ||||
| 	"blog/internal/model/blog" | ||||
| 	"blog/internal/model/vo" | ||||
|  | @ -23,13 +24,13 @@ var ArticleService articleService | |||
| 
 | ||||
| func (*articleService) InitArticleData() { | ||||
| 	var articleSlice []blog.BlogArticle | ||||
| 	result := database.GormTemplate.Where("state = ?", "publish").Find(&articleSlice) | ||||
| 	result := database.GormTemplate.Where("state = ?", consts.ARTICLE_STATE_PUBLISH).Find(&articleSlice) | ||||
| 	log.Println("文章初始化数据加载量:", result.RowsAffected) | ||||
| 	ctx := context.Background() | ||||
| 	for _, article := range articleSlice { | ||||
| 		// log.Println(article)
 | ||||
| 		publishTime := article.PublishTime | ||||
| 		err := database.RedisTemplate.ZAdd(ctx, "blog:article:latest", redis.Z{Score: float64(publishTime), Member: &article}).Err() | ||||
| 		err := database.RedisTemplate.ZAdd(ctx, consts.REDIS_BLOG_ARTICLE_LATEST, redis.Z{Score: float64(publishTime), Member: &article}).Err() | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 		} | ||||
|  | @ -43,14 +44,14 @@ func (*articleService) InitArticleData() { | |||
| 	database.GormTemplate.Table("blog_view_record").Select("rel_id ,count(1) counts").Group("rel_id").Find(&records) | ||||
| 
 | ||||
| 	for _, val := range records { | ||||
| 		database.RedisTemplate.HSet(ctx, "blog:view:record", map[string]any{val.RelId: val.Counts}) | ||||
| 		database.RedisTemplate.HSet(ctx, consts.REDIS_BLOG_VIEW_RECORD, map[string]any{val.RelId: val.Counts}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (*articleService) GetBlogArticle(id string) blog.BlogArticle { | ||||
| 	ctx := context.Background() | ||||
| 	var article blog.BlogArticle | ||||
| 	key := "blog:article:" + id | ||||
| 	key := consts.REDIS_BLOG_ARTICLE + id | ||||
| 	err := database.RedisTemplate.HGetAll(ctx, key).Scan(&article) | ||||
| 	if err != nil || article.Id == "" { | ||||
| 		database.GormTemplate.Table("blog_articles").Where("id = ?", id).First(&article) | ||||
|  | @ -93,7 +94,7 @@ func (*articleService) CreateArticle(articel *admin.AdminArticle) (string, error | |||
| 	articel.CreateTime = time | ||||
| 	articel.UpdateTime = time | ||||
| 	articel.Del = 0 | ||||
| 	articel.State = "draft" | ||||
| 	articel.State = consts.ARTICLE_STATE_DRAFT | ||||
| 
 | ||||
| 	err := database.GormTemplate.Transaction(func(tx *gorm.DB) error { | ||||
| 		contentId := uuid.NewString() | ||||
|  | @ -134,19 +135,19 @@ func (*articleService) PublishArticle(id string) error { | |||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if article.State != "draft" { | ||||
| 	if article.State != consts.ARTICLE_STATE_DRAFT { | ||||
| 		return errors.New("发布失败,文章状态不是起草状态,无法发布") | ||||
| 	} | ||||
| 
 | ||||
| 	article.PublishTime = now | ||||
| 	article.State = "publish" | ||||
| 	article.State = consts.ARTICLE_STATE_PUBLISH | ||||
| 
 | ||||
| 	err = database.GormTemplate.Transaction(func(tx *gorm.DB) error { | ||||
| 		var txErr error = tx.Table("blog_articles").Updates(&article).Error | ||||
| 		if txErr != nil { | ||||
| 			return errors.New("文章更新错误") | ||||
| 		} | ||||
| 		txErr = ContentService.UpdataState(id, "publish") | ||||
| 		txErr = ContentService.UpdataState(id, consts.CONTENT_STATE_PUBLISH) | ||||
| 		if txErr != nil { | ||||
| 			return errors.New("文本状态更新错误") | ||||
| 		} | ||||
|  | @ -156,11 +157,11 @@ func (*articleService) PublishArticle(id string) error { | |||
| 		} | ||||
| 
 | ||||
| 		ctx := context.Background() | ||||
| 		txErr = database.RedisTemplate.ZAdd(ctx, "blog:article:latest", redis.Z{Score: float64(article.PublishTime), Member: &article}).Err() | ||||
| 		txErr = database.RedisTemplate.ZAdd(ctx, consts.REDIS_BLOG_ARTICLE_LATEST, redis.Z{Score: float64(article.PublishTime), Member: &article}).Err() | ||||
| 		if txErr != nil { | ||||
| 			return errors.New("缓存错误") | ||||
| 		} | ||||
| 		txErr = database.RedisTemplate.Set(ctx, "blog:content:"+id, content.Content, time.Duration(0)).Err() | ||||
| 		txErr = database.RedisTemplate.Set(ctx, consts.REDIS_BLOG_CONTENT+id, content.Content, time.Duration(0)).Err() | ||||
| 
 | ||||
| 		return txErr | ||||
| 	}) | ||||
|  | @ -175,14 +176,14 @@ func (*articleService) UnPublishArticle(id string) error { | |||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if article.State != "publish" { | ||||
| 	if article.State != consts.CONTENT_STATE_PUBLISH { | ||||
| 		return errors.New("撤下失败,文章状态不是已发布状态,无法撤下") | ||||
| 	} | ||||
| 
 | ||||
| 	err = database.GormTemplate.Transaction(func(tx *gorm.DB) error { | ||||
| 		var txErr error | ||||
| 		ctx := context.Background() | ||||
| 		txErr = database.RedisTemplate.ZRemRangeByScore(ctx, "blog:article:latest", | ||||
| 		txErr = database.RedisTemplate.ZRemRangeByScore(ctx, consts.REDIS_BLOG_ARTICLE_LATEST, | ||||
| 			strconv.FormatFloat(float64(article.PublishTime), 'E', -1, 64), | ||||
| 			strconv.FormatFloat(float64(article.PublishTime), 'E', -1, 64), | ||||
| 		).Err() | ||||
|  | @ -191,18 +192,18 @@ func (*articleService) UnPublishArticle(id string) error { | |||
| 			return errors.New("缓存错误") | ||||
| 		} | ||||
| 
 | ||||
| 		txErr = database.RedisTemplate.Del(ctx, "blog:content:"+id).Err() | ||||
| 		txErr = database.RedisTemplate.Del(ctx, consts.REDIS_BLOG_CONTENT+id).Err() | ||||
| 		if txErr != nil { | ||||
| 			return errors.New("缓存错误") | ||||
| 		} | ||||
| 
 | ||||
| 		article.State = "draft" | ||||
| 		article.State = consts.ARTICLE_STATE_DRAFT | ||||
| 		article.PublishTime = -1 | ||||
| 		txErr = tx.Table("blog_articles").Updates(&article).Error | ||||
| 		if txErr != nil { | ||||
| 			return errors.New("文章状态更新错误") | ||||
| 		} | ||||
| 		txErr = ContentService.UpdataState(id, "down") | ||||
| 		txErr = ContentService.UpdataState(id, consts.CONTENT_STATE_DOWN) | ||||
| 
 | ||||
| 		return txErr | ||||
| 	}) | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| package service | ||||
| 
 | ||||
| import ( | ||||
| 	"blog/internal/consts" | ||||
| 	"blog/internal/model/vo" | ||||
| 	"blog/third_party/database" | ||||
| 	"context" | ||||
|  | @ -15,12 +16,12 @@ var ContentService contentService | |||
| 
 | ||||
| func (*contentService) InitContentData() { | ||||
| 	var contentSlice []vo.CommonContent | ||||
| 	result := database.GormTemplate.Table("common_contents").Where("state = ?", "publish").Find(&contentSlice) | ||||
| 	result := database.GormTemplate.Table("common_contents").Where("state = ?", consts.CONTENT_STATE_PUBLISH).Find(&contentSlice) | ||||
| 	log.Println("大文本初始化数据加载量:", result.RowsAffected) | ||||
| 	ctx := context.Background() | ||||
| 
 | ||||
| 	for _, content := range contentSlice { | ||||
| 		database.RedisTemplate.Set(ctx, "blog:content:"+content.RelId, content.Content, time.Duration(0)) | ||||
| 		database.RedisTemplate.Set(ctx, consts.REDIS_BLOG_CONTENT+content.RelId, content.Content, time.Duration(0)) | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  | @ -28,7 +29,7 @@ func (*contentService) InitContentData() { | |||
| func (*contentService) GetContentByCache(relId string) string { | ||||
| 	ctx := context.Background() | ||||
| 	var content string | ||||
| 	database.RedisTemplate.Get(ctx, "blog:content:"+relId).Scan(&content) | ||||
| 	database.RedisTemplate.Get(ctx, consts.REDIS_BLOG_CONTENT+relId).Scan(&content) | ||||
| 	return content | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| package service | ||||
| 
 | ||||
| import ( | ||||
| 	"blog/internal/consts" | ||||
| 	"blog/internal/model/blog" | ||||
| 	"blog/third_party/database" | ||||
| 	"context" | ||||
|  | @ -23,7 +24,7 @@ func (*diaryService) InitDiaryData() { | |||
| 	for _, diary := range diarySlice { | ||||
| 		// log.Println(article)
 | ||||
| 		publishTime := diary.PublishTime | ||||
| 		err := database.RedisTemplate.ZAdd(ctx, "blog:diary:latest", redis.Z{Score: float64(publishTime), Member: &diary}).Err() | ||||
| 		err := database.RedisTemplate.ZAdd(ctx, consts.REDIS_BLOG_DIARY_LATEST, redis.Z{Score: float64(publishTime), Member: &diary}).Err() | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 		} | ||||
|  |  | |||
|  | @ -1,9 +1,12 @@ | |||
| package service | ||||
| 
 | ||||
| import ( | ||||
| 	"blog/internal/consts" | ||||
| 	"blog/internal/model/admin" | ||||
| 	"blog/internal/model/vo" | ||||
| 	"blog/third_party/database" | ||||
| 	"context" | ||||
| 	"log" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
|  | @ -12,15 +15,46 @@ type fileService struct { | |||
| 
 | ||||
| var FileService fileService | ||||
| 
 | ||||
| func (*fileService) GetFile(relId string) []byte { | ||||
| func (*fileService) GetFile(id string) (vo.CommonFiles, error) { | ||||
| 	ctx := context.Background() | ||||
| 	key := "blog:file:" + relId | ||||
| 	bytes, err := database.RedisTemplate.Get(ctx, key).Bytes() | ||||
| 	if err != nil { | ||||
| 	var file vo.CommonFiles | ||||
| 		database.GormTemplate.Table("common_files").Where("id = ?", relId).First(&file) | ||||
| 	var bytes []byte | ||||
| 	err := database.RedisTemplate.Get(ctx, consts.REDIS_FILE+id).Scan(&file) | ||||
| 	//如果缓存不存在,则查数据库并存入Redis
 | ||||
| 	if err != nil || file.Id == "" { | ||||
| 		database.GormTemplate.Table("common_files").Where("id = ?", id).First(&file) | ||||
| 		bytes = file.Data | ||||
| 		database.RedisTemplate.Set(ctx, key, bytes, time.Duration(0)) | ||||
| 		err = database.RedisTemplate.Set(ctx, consts.REDIS_FILE+id, &file, time.Duration(0)).Err() | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 		} | ||||
| 	return bytes | ||||
| 		err = database.RedisTemplate.Set(ctx, consts.REDIS_FILE_BYTES+id, bytes, time.Duration(0)).Err() | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 		} | ||||
| 		return file, nil | ||||
| 	} | ||||
| 	bytes, err = database.RedisTemplate.Get(ctx, consts.REDIS_FILE_BYTES+id).Bytes() | ||||
| 	file.Data = bytes | ||||
| 	return file, err | ||||
| } | ||||
| 
 | ||||
| func (*fileService) PageSysFiles(page int, itemsPerPage int) vo.Page[admin.SysFile] { | ||||
| 	var content []admin.SysFile | ||||
| 	var totalElements int64 | ||||
| 	database.GormTemplate.Table("common_files").Count(&totalElements) | ||||
| 	database.GormTemplate.Table("common_files"). | ||||
| 		Where("del != ?", 1). | ||||
| 		Offset((page - 1) * itemsPerPage). | ||||
| 		Limit(itemsPerPage). | ||||
| 		Order("create_time DESC"). | ||||
| 		Find(&content) | ||||
| 
 | ||||
| 	pre := int(totalElements) % itemsPerPage | ||||
| 	if pre > 0 { | ||||
| 		pre = 1 | ||||
| 	} | ||||
| 	var totalPages int = int(totalElements)/itemsPerPage + pre | ||||
| 
 | ||||
| 	return vo.Page[admin.SysFile]{TotalElements: totalElements, TotalPages: totalPages, Number: totalPages, Content: content} | ||||
| } | ||||
|  |  | |||
							
								
								
									
										2
									
								
								main.go
								
								
								
								
							
							
						
						
									
										2
									
								
								main.go
								
								
								
								
							|  | @ -21,6 +21,8 @@ import ( | |||
| // func main() {
 | ||||
| // 	i := time.Now().UnixMilli()
 | ||||
| // 	log.Println(i)
 | ||||
| // 	b, _ := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost)
 | ||||
| // 	log.Println(string(b))
 | ||||
| // }
 | ||||
| 
 | ||||
| func main() { | ||||
|  |  | |||
|  | @ -1,11 +1,12 @@ | |||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| 
 | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <title>Admin Console</title> | ||||
|     <link rel="icon" href="/assets/favicon.ico" type="image/x-icon"/> | ||||
|     <link rel="bookmark" href="/assets/favicon.ico" type="image/x-icon"/> | ||||
|     <link href="/assets/vuetify-v2.6.9/vuetify-v2.6.9.min.css" rel="stylesheet"/> | ||||
|     <link rel="icon" href="/assets/favicon.ico" type="image/x-icon" /> | ||||
|     <link rel="bookmark" href="/assets/favicon.ico" type="image/x-icon" /> | ||||
|     <link href="/assets/vuetify-v2.6.9/vuetify-v2.6.9.min.css" rel="stylesheet" /> | ||||
|     <link href="/assets/vuetify-v2.6.9/materialdesignicons.min.css" rel="stylesheet"> | ||||
|     <link href="/assets/input.css" rel="stylesheet"> | ||||
| 
 | ||||
|  | @ -15,8 +16,9 @@ | |||
|     <script src="/assets/axios/axios.min.js"></script> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"> | ||||
| </head> | ||||
| 
 | ||||
| <body> | ||||
| <div id="app"></div> | ||||
|     <div id="app"></div> | ||||
| </body> | ||||
| <script type="text/x-template" id="app-template"> | ||||
|     <v-app id="inspire"> | ||||
|  | @ -49,6 +51,7 @@ | |||
|                     <v-col cols="12" sm="10"> | ||||
|                         <v-sheet min-height="100%"> | ||||
|                             <textarea id="tinyMceEditor"></textarea> | ||||
|                             <input id="i-files" name="file" type="file" style="display:none"/> | ||||
|                         </v-sheet> | ||||
|                     </v-col> | ||||
|                 </v-row> | ||||
|  | @ -65,8 +68,8 @@ | |||
|     new Vue({ | ||||
|         el: '#app', | ||||
|         template: '#app-template', | ||||
|         computed:{ | ||||
|             tags:{ | ||||
|         computed: { | ||||
|             tags: { | ||||
|                 get() { | ||||
|                     let result = [] | ||||
|                     if (this.article.tags.length == 0) { | ||||
|  | @ -144,16 +147,36 @@ | |||
|                     ],*/ | ||||
|                     fontsize_formats: '12px 14px 16px 18px 24px 36px 48px 56px 72px', | ||||
|                     font_formats: '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats;知乎配置=BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, WenQuanYi Micro Hei, sans-serif;小米配置=Helvetica Neue,Helvetica,Arial,Microsoft Yahei,Hiragino Sans GB,Heiti SC,WenQuanYi Micro Hei,sans-serif', | ||||
|                     link_list: [ | ||||
|                         // { title: '预置链接1', value: 'http://www.tinymce.com' }, | ||||
|                         // { title: '预置链接2', value: 'http://tinymce.ax-z.cn' } | ||||
|                     ], | ||||
|                     // link_list: [ | ||||
|                     //     // { title: '预置链接1', value: 'http://www.tinymce.com' }, | ||||
|                     //     // { title: '预置链接2', value: 'http://tinymce.ax-z.cn' } | ||||
|                     // ], | ||||
| 
 | ||||
|                     //importcss_append: true, | ||||
|                     //自定义文件选择器的回调内容 | ||||
|                     file_picker_callback: function (callback, value, meta) { | ||||
|                         let files = document.getElementById('i-files') | ||||
|                         files.click() | ||||
|                         files.onchange = () => { | ||||
|                             let formData = new FormData(); | ||||
|                             console.log(files.files); | ||||
|                             for (let index = 0; index < files.files.length; index++) { | ||||
|                                 const element = files.files[index]; | ||||
|                                 formData.append("file", element); | ||||
|                             } | ||||
|                             axios.post('/admin/file/upload', formData, { headers: { "Content-Type": "multipart/form-data" } }).then(res => { | ||||
|                                 let data = res.data | ||||
|                                 console.log(data) | ||||
|                                 // location.href = '/diary' | ||||
|                                 callback(`${data.url}`, { text: '附件下载' }); | ||||
|                             }).catch(err => { | ||||
|                                 console.error(err) | ||||
|                             }); | ||||
|                         } | ||||
| 
 | ||||
|                          | ||||
|                         // if (meta.filetype === 'file') { | ||||
|                         //     callback('https://www.baidu.com/img/bd_logo1.png', { text: 'My text' }); | ||||
|                         //     callback('https://www.baidu.com/img/bd_logo1.png', {val:"value", text: 'My text',alt: 'My alt text' }); | ||||
|                         // } | ||||
|                         // if (meta.filetype === 'image') { | ||||
|                         //     callback('https://www.baidu.com/img/bd_logo1.png', { alt: 'My alt text' }); | ||||
|  | @ -177,8 +200,8 @@ | |||
|                         // { title: '预置图片2', value: 'https://www.baidu.com/img/bd_logo1.png' } | ||||
|                     ], | ||||
|                     image_class_list: [ | ||||
|                         {title: 'None', value: ''}, | ||||
|                         {title: 'Some class', value: 'class-name'} | ||||
|                         { title: 'None', value: '' }, | ||||
|                         { title: 'Some class', value: 'class-name' } | ||||
|                     ], | ||||
|                     convert_urls: false, | ||||
|                     images_upload_credentials: true, | ||||
|  | @ -207,4 +230,5 @@ | |||
| 
 | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| </html> | ||||
|  | @ -0,0 +1,201 @@ | |||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| 
 | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <title>Admin Console</title> | ||||
|     <link rel="icon" href="/assets/favicon.ico" type="image/x-icon" /> | ||||
|     <link rel="bookmark" href="/assets/favicon.ico" type="image/x-icon" /> | ||||
|     <link href="/assets/vuetify-v2.6.9/vuetify-v2.6.9.min.css" rel="stylesheet" /> | ||||
|     <link href="/assets/vuetify-v2.6.9/materialdesignicons.min.css" rel="stylesheet"> | ||||
| 
 | ||||
|     <script src="/assets/vue/vue.min.js"></script> | ||||
|     <script src="/assets/vuetify-v2.6.9/vuetify-v2.6.9.min.js"></script> | ||||
|     <script src="/assets/axios/axios.min.js"></script> | ||||
|     <script src="/assets/moment/moment.js"></script> | ||||
|     <script src="/assets/qs/qs.js"></script> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"> | ||||
|     <style> | ||||
|         .over-display { | ||||
|             max-width: 200px; | ||||
|             overflow: hidden; | ||||
|             text-overflow: ellipsis; | ||||
|             white-space: nowrap; | ||||
|             display: inline-block; | ||||
|         } | ||||
|     </style> | ||||
| </head> | ||||
| 
 | ||||
| <body> | ||||
|     <div id="app"></div> | ||||
| </body> | ||||
| <script type="text/x-template" id="app-template"> | ||||
|     <v-app id="inspire"> | ||||
|         #{ render "common/bar-admin.html" . } | ||||
|         <!-- <#include "../../common/drawer-admin.ftl"> --> | ||||
| 
 | ||||
|         <v-dialog v-model="dialog.show" width="500" v-if="dialog.obj"> | ||||
|             <v-card> | ||||
|                 <v-card-title class="text-h5 grey lighten-2">{{dialog.title}}</v-card-title> | ||||
|                 <v-card-text>{{dialog.context}}</v-card-text> | ||||
|                 <v-divider></v-divider> | ||||
|                 <v-card-actions> | ||||
|                     <v-btn text @click="dialog.show = false">取消</v-btn> | ||||
|                     <v-spacer></v-spacer> | ||||
|                     <v-btn color="primary" text @click="confirm">确认</v-btn> | ||||
|                 </v-card-actions> | ||||
|             </v-card> | ||||
|         </v-dialog> | ||||
| 
 | ||||
| 
 | ||||
|         <v-main class="grey lighten-3"> | ||||
|             <v-container style="height: 100%"> | ||||
|                 <v-row style="height: 100%"> | ||||
|                     <!-- <v-col cols="12" sm="2" class=""> | ||||
|                         <v-sheet min-height="100%"> | ||||
|                         </v-sheet> | ||||
|                     </v-col> --> | ||||
| 
 | ||||
|                     <v-col cols="12" sm="12" md="12"> | ||||
|                         <v-sheet min-height="100%"> | ||||
| 
 | ||||
|                             <v-data-table class="elevation-1" | ||||
|                                           :headers="headers" | ||||
|                                           :items="itemList" | ||||
|                                           height="700px" | ||||
|                                           :page.sync="pageable.page" | ||||
|                                           :items-per-page="pageable.itemsPerPage" | ||||
|                                           :loading="loading" | ||||
|                                           loading-text="数据加载中" | ||||
|                                           hide-default-footer> | ||||
|                                 <template #item.createTime="{ item }"> | ||||
|                                     {{item.createTime?moment(item.createTime).format('YYYY/MM/DD HH:mm:ss'):''}} | ||||
|                                 </template> | ||||
|                                 <template #item.fileName="{ item }"> | ||||
|                                     <span class="over-display">{{item.fileName}}</span> | ||||
|                                 </template> | ||||
|                                 <template #item.fileSize="{ item }"> | ||||
|                                     {{Math.round(item.fileSize/1024)}}KB | ||||
|                                 </template> | ||||
| 
 | ||||
|                                 <template #item.actions="{ item }"> | ||||
|                                     <v-btn text color="primary" :href="`/file/${item.id}`" target="_blank">下载</v-btn> | ||||
|                                 </template> | ||||
|                             </v-data-table> | ||||
|                             <div class="pt-2 float-right" style="display: flex"> | ||||
|                                 <v-pagination v-model="pageable.page" :length="pageable.totalPages" :total-visible="7" | ||||
|                                               style="min-width: 200px;" @previous="previousPage" @next="nextPage" | ||||
|                                               @input="goPage"></v-pagination> | ||||
|                                 <v-select :items="[5,10,20,50,100]" label="分页大小" style="width: 100px" | ||||
|                                           v-model="pageable.itemsPerPage" outlined dense | ||||
|                                           @input="pageSizeChange"></v-select> | ||||
|                             </div> | ||||
|                         </v-sheet> | ||||
|                     </v-col> | ||||
| 
 | ||||
|                     <!-- <v-col cols="12" sm="2"> | ||||
|                         <v-sheet min-height="100%"> | ||||
|                         </v-sheet> | ||||
|                     </v-col> --> | ||||
| 
 | ||||
| 
 | ||||
|                 </v-row> | ||||
|             </v-container> | ||||
|         </v-main> | ||||
|     </v-app> | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
| <script> | ||||
|     var qs = Qs | ||||
| 
 | ||||
|     new Vue({ | ||||
|         el: '#app', | ||||
|         template: '#app-template', | ||||
|         data: { | ||||
|             group: null, | ||||
|             drawer: false, | ||||
|             loading: false, | ||||
|             dialog: { | ||||
|                 show: false, | ||||
|                 title: '', | ||||
|                 context: '', | ||||
|                 obj: null | ||||
|             }, | ||||
|             pageable: { | ||||
|                 page: 1, | ||||
|                 itemsPerPage: 20, | ||||
|                 totalElements: 0, | ||||
|                 totalPages: 1 | ||||
|             }, | ||||
|             param: { | ||||
|                 id: null, | ||||
|                 title: null, | ||||
|                 subTitle: null, | ||||
|             }, | ||||
| 
 | ||||
|             headers: [ | ||||
|                 { text: 'ID', value: 'id', align: 'start', sortable: false }, | ||||
|                 { text: '文件名', value: 'fileName', align: 'start', sortable: false }, | ||||
|                 { text: '文件大小', value: 'fileSize', align: 'start', sortable: false }, | ||||
|                 { text: '上传人', value: 'createBy', align: 'start', sortable: false }, | ||||
|                 { text: '上传时间', value: 'createTime', align: 'start', sortable: false }, | ||||
|                 { text: '操作', value: 'actions', align: 'center', sortable: false }, | ||||
|             ], | ||||
|             itemList: [], | ||||
|         }, | ||||
|         methods: { | ||||
|             pageSizeChange(size) { | ||||
|                 this.pageable.page = 1 | ||||
|                 this.list() | ||||
|             }, | ||||
|             nextPage() { | ||||
|                 this.pageable.page = this.pageable.page + 1 | ||||
|                 // this.list() | ||||
|             }, | ||||
|             previousPage() { | ||||
|                 this.pageable.page = this.pageable.page - 1 | ||||
|                 // this.list() | ||||
|             }, | ||||
|             goPage(num) { | ||||
|                 this.pageable.page = num | ||||
|                 this.list() | ||||
|             }, | ||||
|             list() { | ||||
|                 let ths = this; | ||||
|                 this.loading = true; | ||||
|                 let { page, itemsPerPage } = this.pageable; | ||||
|                 let pageQuery = qs.stringify({ page, itemsPerPage, ...this.param }, { skipNulls: true }); | ||||
|                 // let paramQuery = qs.stringify(this.param, {skipNulls: true}); | ||||
|                 axios.get('/admin/file/list?' + pageQuery).then(function (response) { | ||||
|                     // console.log(response); | ||||
|                     let data = response.data; | ||||
|                     ths.itemList = data.content; | ||||
|                     ths.pageable.totalElements = data.totalElements | ||||
|                     ths.pageable.totalPages = data.totalPages | ||||
|                     ths.pageable.page = data.number + 1 | ||||
| 
 | ||||
|                     ths.loading = false; | ||||
|                 }).catch(function (error) { | ||||
|                     console.log(error); | ||||
|                     ths.loading = false; | ||||
|                 }); | ||||
|             }, | ||||
| 
 | ||||
|             download(id) { | ||||
|                 console.log(id); | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|         }, | ||||
|         mounted() { | ||||
|             this.list(); | ||||
| 
 | ||||
|         }, | ||||
| 
 | ||||
|         vuetify: new Vuetify(), | ||||
|     }) | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| </html> | ||||
		Loading…
	
		Reference in New Issue