From d0bb3744144276d0b64575f2f6a15e187b8d27e8 Mon Sep 17 00:00:00 2001 From: wucongxing8150 <815046773@qq.com> Date: Mon, 9 Sep 2024 13:59:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=86=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E5=99=A8=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/controller/Base.go | 1 + api/controller/Editor.go | 136 +++++++++++++++++++++++++++++++++++++++ api/router/router.go | 10 +++ extend/Editor/Editor.go | 128 ++++++++++++++++++++++++++++++++++++ utils/replace.go | 10 +++ 5 files changed, 285 insertions(+) create mode 100644 api/controller/Editor.go create mode 100644 extend/Editor/Editor.go diff --git a/api/controller/Base.go b/api/controller/Base.go index 9bc954b..0f9d7da 100644 --- a/api/controller/Base.go +++ b/api/controller/Base.go @@ -10,4 +10,5 @@ type Api struct { UserVoteDay // 投票记录 System // 配置 BaseHospital // 基础数据-医院 + Editor // 配置-编辑器 } diff --git a/api/controller/Editor.go b/api/controller/Editor.go new file mode 100644 index 0000000..31f1ab9 --- /dev/null +++ b/api/controller/Editor.go @@ -0,0 +1,136 @@ +package controller + +import ( + "fmt" + "github.com/gin-gonic/gin" + "io" + "math/rand" + "mime/multipart" + "net/http" + "time" + "vote-admin-api/api/responses" + e "vote-admin-api/extend/Editor" + "vote-admin-api/extend/aliyun" + "vote-admin-api/utils" +) + +type Editor struct{} + +// GetEditorConfig 编辑器-获取配置 +func (b *Editor) GetEditorConfig(c *gin.Context) { + action := c.Query("action") + if action == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 获取配置 config + if action == "config" { + config := e.GetConfig() + c.JSON(http.StatusOK, config) + return + } + + // 图片列表 listImage + if action == "listImage" { + responses.Ok(c) + return + } + + // 文件列表 listFile + if action == "listFile" { + responses.Ok(c) + return + } + + responses.Ok(c) + return +} + +// EditorUpload 编辑器-上传 +func (b *Editor) EditorUpload(c *gin.Context) { + action := c.Query("action") + if action == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + file, err := c.FormFile("file") + if err != nil { + responses.FailWithMessage("文件错误", c) + return + } + + f, err := file.Open() + if err != nil { + responses.FailWithMessage("文件错误", c) + return + } + defer func(f multipart.File) { + err := f.Close() + if err != nil { + fmt.Println(err) + } + }(f) + + // 读取文件内容到字节切片 + fileBytes, err := io.ReadAll(f) + if err != nil { + responses.FailWithMessage("文件错误", c) + return + } + + fileType := c.PostForm("type") + if fileType == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + fileType = utils.GetExtension(fileType) + if fileType == "" { + fileType = "jpg" + } + + now := time.Now() + dateTimeString := now.Format("20060102150405") // 当前时间字符串 + rand.New(rand.NewSource(time.Now().UnixNano())) // 设置随机数 + + var ossPath string + + // 上传图片 image + if action == "image" { + ossPath = "static/images/" + fmt.Sprintf("%d", rand.Intn(9000)+1000) + dateTimeString + "." + fileType + } + + // 上传视频 video + if action == "video" { + ossPath = "static/video/" + fmt.Sprintf("%d", rand.Intn(9000)+1000) + dateTimeString + "." + fileType + } + + // 上传文件 file + if action == "file" { + ossPath = "static/file/" + fmt.Sprintf("%d", rand.Intn(9000)+1000) + dateTimeString + "." + fileType + } + + if ossPath == "" { + responses.FailWithMessage("上传失败", c) + return + } + + // 上传oss + _, err = aliyun.PutObjectByte(ossPath, fileBytes) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + var g e.UploadDto + g.Url = utils.AddOssDomain("/" + ossPath) + g.State = "SUCCESS" + g.Title = dateTimeString + "." + fileType + g.Original = dateTimeString + "." + fileType + g.Type = fileType + + c.JSON(http.StatusOK, g) + return +} diff --git a/api/router/router.go b/api/router/router.go index 77d4c7a..288efa4 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -205,5 +205,15 @@ func privateRouter(r *gin.Engine, api controller.Api) { // 修改投票时间 timeGroup.PUT("/:system_time_id", api.System.PutSystemTime) } + + // 编辑器配置 + editorGroup := systemGroup.Group("/editor") + { + // 编辑器-获取配置 + editorGroup.GET("", api.Editor.GetEditorConfig) + + // 编辑器-上传 + editorGroup.POST("", api.Editor.EditorUpload) + } } } diff --git a/extend/Editor/Editor.go b/extend/Editor/Editor.go new file mode 100644 index 0000000..c1e8d3c --- /dev/null +++ b/extend/Editor/Editor.go @@ -0,0 +1,128 @@ +package Editor + +// Config 定义了上传和管理图片、视频、文件等的配置信息 +type Config struct { + // 图片上传配置 + ImageActionName string `json:"imageActionName"` // 执行上传图片的action名称,默认值:image + ImageFieldName string `json:"imageFieldName"` // 提交的图片表单名称,默认值:file + ImageMaxSize int `json:"imageMaxSize"` // 上传大小限制,单位B,默认值:2048000 + ImageAllowFiles []string `json:"imageAllowFiles"` // 允许的图片格式,默认值:[".png", ".jpg", ".jpeg"] + ImageCompressEnable bool `json:"imageCompressEnable"` // 是否压缩图片,默认值:true + ImageCompressBorder int `json:"imageCompressBorder"` // 图片压缩最长边限制,默认值:1600 + ImageInsertAlign string `json:"imageInsertAlign"` // 插入图片时的浮动方式,默认值:none + ImageUrlPrefix string `json:"imageUrlPrefix"` // 图片访问路径前缀,默认值:空 + + // 涂鸦上传配置 + ScrawlActionName string `json:"scrawlActionName"` // 执行上传涂鸦的action名称,默认值:scrawl + ScrawlFieldName string `json:"scrawlFieldName"` // 提交的涂鸦表单名称,默认值:file + ScrawlMaxSize int `json:"scrawlMaxSize"` // 涂鸦上传大小限制,单位B,默认值:2048000 + ScrawlUrlPrefix string `json:"scrawlUrlPrefix"` // 涂鸦访问路径前缀,默认值:空 + ScrawlInsertAlign string `json:"scrawlInsertAlign"` // 插入涂鸦时的浮动方式,默认值:none + + // 截图上传配置 + SnapscreenActionName string `json:"snapscreenActionName"` // 执行上传截图的action名称,默认值:snap + SnapscreenUrlPrefix string `json:"snapscreenUrlPrefix"` // 截图访问路径前缀,默认值:空 + SnapscreenInsertAlign string `json:"snapscreenInsertAlign"` // 插入截图时的浮动方式,默认值:none + + // 图片抓取配置 + CatcherActionName string `json:"catcherActionName"` // 执行抓取远程图片的action名称,默认值:catch + CatcherFieldName string `json:"catcherFieldName"` // 提交的图片列表表单名称,默认值:source + CatcherLocalDomain []string `json:"catcherLocalDomain"` // 例外的图片抓取域名,默认值:["127.0.0.1", "localhost"] + CatcherUrlPrefix string `json:"catcherUrlPrefix"` // 抓取图片访问路径前缀,默认值:空 + CatcherMaxSize int `json:"catcherMaxSize"` // 抓取图片上传大小限制,单位B,默认值:10485760 + CatcherAllowFiles []string `json:"catcherAllowFiles"` // 允许的抓取图片格式,默认值:[".png", ".jpg", ".jpeg"] + + // 视频上传配置 + VideoActionName string `json:"videoActionName"` // 执行上传视频的action名称,默认值:video + VideoFieldName string `json:"videoFieldName"` // 提交的视频表单名称,默认值:file + VideoUrlPrefix string `json:"videoUrlPrefix"` // 视频访问路径前缀,默认值:空 + VideoMaxSize int `json:"videoMaxSize"` // 上传视频大小限制,单位B,默认值:102400000 + VideoAllowFiles []string `json:"videoAllowFiles"` // 允许的视频格式,默认值:[".mp4"] + + // 文件上传配置 + FileActionName string `json:"fileActionName"` // 执行上传文件的action名称,默认值:file + FileFieldName string `json:"fileFieldName"` // 提交的文件表单名称,默认值:file + FileUrlPrefix string `json:"fileUrlPrefix"` // 文件访问路径前缀,默认值:空 + FileMaxSize int `json:"fileMaxSize"` // 上传文件大小限制,单位B,默认值:104857600 + FileAllowFiles []string `json:"fileAllowFiles"` // 允许的文件格式,默认值:[".zip", ".pdf", ".doc"] + + // 图片列表配置 + ImageManagerActionName string `json:"imageManagerActionName"` // 执行图片管理的action名称,默认值:listImage + ImageManagerListSize int `json:"imageManagerListSize"` // 每次列出文件数量,默认值:20 + ImageManagerUrlPrefix string `json:"imageManagerUrlPrefix"` // 图片管理访问路径前缀,默认值:空 + ImageManagerInsertAlign string `json:"imageManagerInsertAlign"` // 插入的图片浮动方式,默认值:none + ImageManagerAllowFiles []string `json:"imageManagerAllowFiles"` // 列出的图片文件类型,默认值:[".jpg", ".png", ".jpeg"] + + // 文件列表配置 + FileManagerActionName string `json:"fileManagerActionName"` // 执行文件管理的action名称,默认值:listFile + FileManagerUrlPrefix string `json:"fileManagerUrlPrefix"` // 指定要列出文件的目录,默认值:空 + FileManagerListSize int `json:"fileManagerListSize"` // 每次列出文件数量,默认值:20 + FileManagerAllowFiles []string `json:"fileManagerAllowFiles"` // 列出的文件类型,默认值:[".zip", ".pdf", ".doc"] + + // 公式配置 + FormulaConfig FormulaConfig `json:"formulaConfig"` // 公式渲染相关配置 +} + +// FormulaConfig 定义了公式渲染的相关配置 +type FormulaConfig struct { + ImageUrlTemplate string `json:"imageUrlTemplate"` // 公式渲染的图片URL模板 +} + +// UploadDto 上传返回数据 +type UploadDto struct { + Original string `json:"original"` + State string `json:"state"` + Title string `json:"title"` + Type string `json:"type"` + Url string `json:"url"` +} + +// GetConfig 封装方法,返回配置信息 +func GetConfig() Config { + return Config{ + ImageActionName: "image", + ImageFieldName: "file", + ImageMaxSize: 10485760, + ImageAllowFiles: []string{".jpg", ".png", ".jpeg"}, + ImageCompressEnable: true, + ImageCompressBorder: 5000, + ImageInsertAlign: "none", + ImageUrlPrefix: "", + ScrawlActionName: "crawl", + ScrawlFieldName: "file", + ScrawlMaxSize: 10485760, + ScrawlUrlPrefix: "", + ScrawlInsertAlign: "none", + SnapscreenActionName: "snap", + SnapscreenUrlPrefix: "", + SnapscreenInsertAlign: "none", + CatcherActionName: "catch", + CatcherFieldName: "source", + CatcherLocalDomain: []string{"127.0.0.1", "localhost"}, + CatcherUrlPrefix: "", + CatcherMaxSize: 10485760, + CatcherAllowFiles: []string{".jpg", ".png", ".jpeg"}, + VideoActionName: "video", + VideoFieldName: "file", + VideoUrlPrefix: "", + VideoMaxSize: 104857600, + VideoAllowFiles: []string{".mp4"}, + FileActionName: "file", + FileFieldName: "file", + FileUrlPrefix: "", + FileMaxSize: 104857600, + FileAllowFiles: []string{".zip", ".pdf", ".doc"}, + ImageManagerActionName: "listImage", + ImageManagerListSize: 20, + ImageManagerUrlPrefix: "", + ImageManagerInsertAlign: "none", + ImageManagerAllowFiles: []string{".jpg", ".png", ".jpeg"}, + FileManagerActionName: "listFile", + FileManagerUrlPrefix: "", + FileManagerListSize: 20, + FileManagerAllowFiles: []string{".zip", ".pdf", ".doc"}, + FormulaConfig: FormulaConfig{ + ImageUrlTemplate: "https://r.latexeasy.com/image.svg?{}", + }, + } +} diff --git a/utils/replace.go b/utils/replace.go index 6cf1519..ee4acd3 100644 --- a/utils/replace.go +++ b/utils/replace.go @@ -20,3 +20,13 @@ func AddOssDomain(url string) string { } return config.C.Oss.OssCustomDomainName + url } + +// GetExtension 解析并返回字符串中 `/` 后面的部分 +func GetExtension(s string) string { + // 使用 strings.Split 分割字符串 + parts := strings.Split(s, "/") + if len(parts) > 1 { + return parts[1] // 返回第二部分 + } + return "" +}