فهرست منبع

Merge branch 'debug'

kobe6258 3 ماه پیش
والد
کامیت
5b1918d764

+ 36 - 21
controllers/analyst.go

@@ -5,8 +5,8 @@ import (
 	"eta/eta_mini_crm_ht/models"
 	"eta/eta_mini_crm_ht/models/request"
 	"eta/eta_mini_crm_ht/models/response"
-	"eta/eta_mini_crm_ht/services"
 	"eta/eta_mini_crm_ht/utils"
+	"eta/eta_mini_crm_ht/utils/oss"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"os"
@@ -41,12 +41,13 @@ func (this *AnalystController) UploadImage() {
 		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
 		return
 	}
+	if !utils.CheckImageFormat(h.Filename, f, h) {
+		br.Msg = "图片上传失败"
+		br.ErrMsg = "图片上传失败,不支持的文件格式"
+		return
+	}
 	defer f.Close()
 	ext := path.Ext(h.Filename)
-	//if ext != ".pdf" {
-	//	br.Msg = "文件格式不正确"
-	//	return
-	//}
 	size, err := strconv.Atoi(utils.UPLOAD_IMG_SIZE)
 	if err != nil {
 		size = 15
@@ -55,8 +56,8 @@ func (this *AnalystController) UploadImage() {
 		br.Msg = fmt.Sprintf("头像文件大小不能超过%dK", size)
 		return
 	}
-	dateDir := time.Now().Format("20060102")
-	uploadDir := utils.STATIC_DIR + "ht/" + dateDir
+	dateDir := time.Now().Format(time.DateOnly)
+	uploadDir := utils.STATIC_DIR + "ht/analyst/" + dateDir
 	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		br.Msg = "存储目录创建失败"
@@ -72,33 +73,46 @@ func (this *AnalystController) UploadImage() {
 		br.ErrMsg = "头像上传失败,Err:" + err.Error()
 		return
 	}
-	pdfUploadDir := utils.RESOURCE_DIR + "analyst/"
-	savePdfToOssPath := pdfUploadDir + time.Now().Format("200601/20060102/")
+	thumbnailPath, err := utils.ImageResize(fpath, 1200)
+	if err != nil {
+		br.Msg = "头像上传失败"
+		br.ErrMsg = "头像上传失败,Err:" + err.Error()
+		return
+	}
+	ossUploadDir := utils.RESOURCE_DIR + "analyst/"
+	saveOssPath := ossUploadDir + time.Now().Format("200601/20060102/")
 	imgName := utils.GetRandStringNoSpecialChar(28)
-	savePdfToOssPath += imgName
-
+	saveOssPath += imgName
 	defer func() {
 		_ = os.Remove(fpath)
+		_ = os.Remove(thumbnailPath)
 	}()
-
-	ossClient := services.NewOssClient()
+	ossClient := oss.NewOssClient()
 	if ossClient == nil {
 		br.Msg = "头像上传失败"
 		br.ErrMsg = "初始化OSS服务失败"
 		return
 	}
-	imgUrl, err := ossClient.UploadFile("", fpath, savePdfToOssPath)
+	//新增上传缩略图
+	thumbnailUrl, err := ossClient.UploadFile("", thumbnailPath, saveOssPath+"_thumbnail")
+	if err != nil {
+		br.Msg = "头像上传失败"
+		br.ErrMsg = "头像上传失败,Err:" + err.Error()
+		return
+	}
+	imgUrl, err := ossClient.UploadFile("", fpath, saveOssPath)
 	if err != nil {
 		br.Msg = "头像上传失败"
 		br.ErrMsg = "头像上传失败,Err:" + err.Error()
 		return
 	}
-
 	base := path.Base(h.Filename)
-	resp := new(response.ReportPdfUploadResp)
-	resp.Url = imgUrl
-	resp.FileName = base
-
+	thumbnailBase := path.Base(h.Filename + "_thumbnail")
+	resp := new(response.HeadImageUploadResp)
+	resp.Url = thumbnailUrl
+	resp.OriginUrl = imgUrl
+	resp.FileName = thumbnailBase
+	resp.OriginFileName = base
 	br.Data = resp
 	br.Msg = "上传成功"
 	br.Ret = 200
@@ -118,7 +132,6 @@ func (this *AnalystController) GetAnalystByName(AnalystName string) {
 		this.ServeJSON()
 	}()
 	analyst, err := models.GetAnalystByName(AnalystName)
-
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取失败,Err:" + err.Error()
@@ -177,6 +190,7 @@ func (this *AnalystController) List() {
 			Id:                      v.Id,
 			Name:                    v.Name,
 			HeadImgURL:              v.HeadImgUrl,
+			HeadOriginImgUrl:        v.HeadOriginImgUrl,
 			Position:                v.Position,
 			InvestmentCertificate:   v.InvestmentCertificate,
 			ProfessionalCertificate: v.ProfessionalCertificate,
@@ -241,10 +255,11 @@ func (this *AnalystController) Edit() {
 	}
 	analyst.Introduction = req.Introduction
 	analyst.Position = req.Position
+	analyst.HeadOriginImgUrl = req.HeadOriginImgUrl
 	analyst.ProfessionalCertificate = req.ProfessionalCertificate
 	analyst.InvestmentCertificate = req.InvestmentCertificate
 	analyst.UpdatedTime = time.Now()
-	err = analyst.Update([]string{"HeadImgURL", "Introduction", "Position", "InvestmentCertificate", "ProfessionalCertificate", "UpdatedTime"})
+	err = analyst.Update([]string{"HeadImgURL", "HeadOriginImgURL", "Introduction", "Position", "InvestmentCertificate", "ProfessionalCertificate", "UpdatedTime"})
 	if err != nil {
 		br.Msg = "编辑失败"
 		br.ErrMsg = "编辑失败, 系统错误,Err:" + err.Error()

+ 82 - 21
controllers/audio.go

@@ -1,22 +1,33 @@
 package controllers
 
 import (
+	"encoding/binary"
 	"encoding/json"
 	"eta/eta_mini_crm_ht/models"
 	"eta/eta_mini_crm_ht/models/request"
 	"eta/eta_mini_crm_ht/models/response"
 	"eta/eta_mini_crm_ht/services"
-	"eta/eta_mini_crm_ht/services/elastic"
 	"eta/eta_mini_crm_ht/utils"
+	"eta/eta_mini_crm_ht/utils/elastic"
+	"eta/eta_mini_crm_ht/utils/oss"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"github.com/tcolgate/mp3"
+	"mime/multipart"
 	"os"
 	"path"
 	"strconv"
+	"strings"
 	"time"
 )
 
+var (
+	audioType = map[string]int{
+		".mp3": 1,
+		".m4a": 1,
+	}
+)
+
 type AudioController struct {
 	BaseAuthController
 }
@@ -49,11 +60,24 @@ func (this *AudioController) UploadAudio() {
 		return
 	}
 	ext := path.Ext(h.Filename)
-	if ext != ".mp3" {
+	lowCaseExt := strings.ToLower(ext)
+	if _, ok := audioType[lowCaseExt]; !ok {
 		br.Msg = "音频格式不正确"
 		br.ErrMsg = "音频上传失败,Err:" + err.Error()
 		return
 	}
+	var duration int
+	if lowCaseExt == ".m4a" {
+		duration, err = m4aDuration(f)
+	}
+	if lowCaseExt == ".mp3" {
+		duration, err = mp3Duration(f)
+	}
+	if err != nil {
+		br.Msg = "音频上传失败"
+		br.ErrMsg = "解析音频时常失败,Err:" + err.Error()
+		return
+	}
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "ht/audio" + dateDir
 	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
@@ -71,22 +95,6 @@ func (this *AudioController) UploadAudio() {
 		br.ErrMsg = "音频上传失败,Err:" + err.Error()
 		return
 	}
-	mp3File := mp3.NewDecoder(f)
-	if err != nil {
-		br.Msg = "音频上传失败"
-		br.ErrMsg = "解析音频时常失败,Err:" + err.Error()
-		return
-	}
-	var totalDuration time.Duration
-	skipped := 0
-	for {
-		frame := mp3.Frame{}
-		if err = mp3File.Decode(&frame, &skipped); err != nil {
-			break
-		}
-		totalDuration += frame.Duration()
-	}
-	duration := int(totalDuration.Milliseconds())
 
 	audioUploadDir := utils.RESOURCE_DIR + "audio/"
 	savePdfToOssPath := audioUploadDir + time.Now().Format("200601/20060102/")
@@ -97,7 +105,7 @@ func (this *AudioController) UploadAudio() {
 		err = os.Remove(fpath)
 		fmt.Sprintf("删除文件失败:%v", err)
 	}()
-	ossClient := services.NewOssClient()
+	ossClient := oss.NewOssClient()
 	if ossClient == nil {
 		br.Msg = "音频上传失败"
 		br.ErrMsg = "初始化OSS服务失败"
@@ -109,6 +117,7 @@ func (this *AudioController) UploadAudio() {
 		br.ErrMsg = "音频上传失败,Err:" + err.Error()
 		return
 	}
+
 	base := path.Base(h.Filename)
 	resp := new(response.MediaUploadResp)
 	resp.Url = mp3Url
@@ -119,6 +128,59 @@ func (this *AudioController) UploadAudio() {
 	br.Ret = 200
 	br.Success = true
 }
+func mp3Duration(file multipart.File) (duration int, err error) {
+	mp3File := mp3.NewDecoder(file)
+
+	var totalDuration time.Duration
+	skipped := 0
+	for {
+		frame := mp3.Frame{}
+		if err = mp3File.Decode(&frame, &skipped); err != nil {
+			break
+		}
+		totalDuration += frame.Duration()
+	}
+	duration = int(totalDuration.Milliseconds())
+	return
+}
+func m4aDuration(file multipart.File) (duration int, err error) {
+	var info = make([]byte, 0x10)
+	var boxHeader BoxHeader
+	var offset int64 = 0
+	// 获取moov结构偏移
+	for {
+		_, err = file.ReadAt(info, offset)
+		if err != nil {
+			return
+		}
+		boxHeader = getHeaderBoxInfo(info)
+		fourccType := getFourccType(boxHeader)
+		if fourccType == "moov" {
+			break
+		}
+		// 有一部分mp4 mdat尺寸过大需要特殊处理
+		if fourccType == "mdat" {
+			if boxHeader.Size == 1 {
+				offset += int64(boxHeader.Size64)
+				continue
+			}
+		}
+		offset += int64(boxHeader.Size)
+	}
+	// 获取moov结构开头一部分
+	moovStartBytes := make([]byte, 0x100)
+	_, err = file.ReadAt(moovStartBytes, offset)
+	if err != nil {
+		return
+	}
+	// 定义timeScale与Duration偏移
+	timeScaleOffset := 0x1C
+	durationOffest := 0x20
+	timeScale := binary.BigEndian.Uint32(moovStartBytes[timeScaleOffset : timeScaleOffset+4])
+	Duration := binary.BigEndian.Uint32(moovStartBytes[durationOffest : durationOffest+4])
+	duration = int(Duration * 1000 / timeScale)
+	return
+}
 
 // AddAudio @Title 新增音频
 // @Description 新增音频
@@ -160,14 +222,13 @@ func (this *AudioController) AddAudio() {
 		return
 	}
 	var err error
-
 	audioInsert := &models.Media{
 		AuthorId:              req.AnalystId,
 		AuthorName:            req.AnalystName,
 		MediaType:             models.Audio,
 		Src:                   req.SrcUrl,
 		MediaName:             req.AudioName,
-		SourceType:            "mp3",
+		SourceType:            req.SrcUrl[strings.LastIndex(req.SrcUrl, ".")+1:],
 		MediaPlayMilliseconds: req.DurationMillisecond,
 		PermissionIds:         req.PermissionIds,
 		SendStatus:            models.UNSEND,

+ 10 - 10
controllers/image.go

@@ -5,8 +5,8 @@ import (
 	"eta/eta_mini_crm_ht/models"
 	"eta/eta_mini_crm_ht/models/request"
 	"eta/eta_mini_crm_ht/models/response"
-	"eta/eta_mini_crm_ht/services"
 	"eta/eta_mini_crm_ht/utils"
+	"eta/eta_mini_crm_ht/utils/oss"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"os"
@@ -37,6 +37,11 @@ func (this *ImageController) UploadImage() {
 		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
 		return
 	}
+	if !utils.CheckImageFormat(h.Filename, f, h) {
+		br.Msg = "图片上传失败"
+		br.ErrMsg = "图片上传失败,不支持的文件格式"
+		return
+	}
 	defer f.Close()
 	size, err := strconv.Atoi(utils.UPLOAD_IMG_SIZE)
 	if err != nil {
@@ -48,13 +53,8 @@ func (this *ImageController) UploadImage() {
 		return
 	}
 	ext := path.Ext(h.Filename)
-	//if ext != ".mp3" {
-	//	br.Msg = "音频格式不正确"
-	//	br.ErrMsg = "音频上传失败,Err:" + err.Error()
-	//	return
-	//}
-	dateDir := time.Now().Format("20060102")
-	uploadDir := utils.STATIC_DIR + "ht/audio" + dateDir
+	dateDir := time.Now().Format(time.DateOnly)
+	uploadDir := utils.STATIC_DIR + "ht/image" + dateDir
 	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		br.Msg = "存储目录创建失败"
@@ -79,7 +79,7 @@ func (this *ImageController) UploadImage() {
 		err = os.Remove(fpath)
 		fmt.Sprintf("删除文件失败:%v", err)
 	}()
-	ossClient := services.NewOssClient()
+	ossClient := oss.NewOssClient()
 	if ossClient == nil {
 		br.Msg = "图片上传失败"
 		br.ErrMsg = "初始化OSS服务失败"
@@ -125,7 +125,7 @@ func (this *ImageController) AddImage() {
 	}
 
 	if req.SrcUrl == "" {
-		br.Msg = "音频地址为空"
+		br.Msg = "图片名称为空"
 		return
 	}
 

+ 4 - 4
controllers/image_back.go

@@ -5,9 +5,9 @@ import (
 	"eta/eta_mini_crm_ht/models"
 	"eta/eta_mini_crm_ht/models/request"
 	"eta/eta_mini_crm_ht/models/response"
-	"eta/eta_mini_crm_ht/services"
-	"eta/eta_mini_crm_ht/services/elastic"
 	"eta/eta_mini_crm_ht/utils"
+	"eta/eta_mini_crm_ht/utils/elastic"
+	"eta/eta_mini_crm_ht/utils/oss"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
 
@@ -208,7 +208,7 @@ func (this *ImagePDFController) UploadPdf() {
 		_ = os.Remove(fpath)
 	}()
 
-	ossClient := services.NewOssClient()
+	ossClient := oss.NewOssClient()
 	if ossClient == nil {
 		br.Msg = "文件上传失败"
 		br.ErrMsg = "初始化OSS服务失败"
@@ -724,7 +724,7 @@ func (this *ImagePDFController) UploadImage() {
 		_ = os.Remove(fpath)
 	}()
 
-	ossClient := services.NewOssClient()
+	ossClient := oss.NewOssClient()
 	if ossClient == nil {
 		br.Msg = "图片上传失败"
 		br.ErrMsg = "初始化OSS服务失败"

+ 7 - 1
controllers/product.go

@@ -8,6 +8,7 @@ import (
 	"eta/eta_mini_crm_ht/models/response"
 	"eta/eta_mini_crm_ht/services"
 	"eta/eta_mini_crm_ht/utils"
+	"eta/eta_mini_crm_ht/utils/oss"
 	"fmt"
 	"github.com/go-sql-driver/mysql"
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -714,6 +715,11 @@ func (this *ProductController) UploadFile() {
 		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
 		return
 	}
+	if !utils.CheckImageFormat(h.Filename, f, h) {
+		br.Msg = "图片上传失败"
+		br.ErrMsg = "图片上传失败,不支持的文件格式"
+		return
+	}
 	defer f.Close()
 	size, err := strconv.Atoi(utils.UPLOAD_IMG_SIZE)
 	if err != nil {
@@ -752,7 +758,7 @@ func (this *ProductController) UploadFile() {
 		err = os.Remove(fpath)
 		fmt.Sprintf("删除文件失败:%v", err)
 	}()
-	ossClient := services.NewOssClient()
+	ossClient := oss.NewOssClient()
 	if ossClient == nil {
 		br.Msg = "图片上传失败"
 		br.ErrMsg = "初始化OSS服务失败"

+ 34 - 28
controllers/sys_user.go

@@ -487,7 +487,7 @@ func (this *SysUserController) ResetPass() {
 		br.Msg = "密码格式不对,必须包含8位及以上,包含数字、大写字母、小写字母、特殊字符中的三个类型"
 		return
 	}
-	pwd = utils.MD5(pwd)
+	//pwd = utils.MD5(pwd)
 	sysUser.Password = pwd
 	sysUser.IsEnabled = true
 	sysUser.ModifyTime = time.Now()
@@ -529,35 +529,41 @@ func (this *SysUserController) ResetMyPass() {
 
 	sysUser := this.SysUser
 	// 校验系统用户的密码
-	var pwd string
-	{
-		pwdByte, err := base64.StdEncoding.DecodeString(req.Password)
-		if err != nil {
-			br.Msg = "解析数据失败"
-			br.ErrMsg = "解析数据失败,Err:" + err.Error()
-			return
-		}
-		originPwd := string(pwdByte)
-		if !utils.CheckPwd(originPwd) {
-			br.Msg = "密码格式错误,请重新输入"
-			return
-		}
-		pwd = utils.MD5(originPwd)
-
-		originPwdByte, err := base64.StdEncoding.DecodeString(req.OriginPassword)
-		if err != nil {
-			br.Msg = "解析数据失败"
-			br.ErrMsg = "解析数据失败,Err:" + err.Error()
-			return
-		}
-		stringPwd := string(originPwdByte)
-		if sysUser.Password != utils.MD5(stringPwd) {
-			br.Msg = "原密码错误"
-			return
-		}
+	//var pwd string
+	//{
+	//pwdByte, err := base64.StdEncoding.DecodeString(req.Password)
+	//if err != nil {
+	//	br.Msg = "解析数据失败"
+	//	br.ErrMsg = "解析数据失败,Err:" + err.Error()
+	//	return
+	//}
+	//originPwd := string(pwdByte)
+	//if !utils.CheckPwd(originPwd) {
+	//	br.Msg = "密码格式错误,请重新输入"
+	//	return
+	//}
+	//前端直接加密,后端不做处理
+	//pwd = utils.MD5(originPwd)
+	//pwd = originPwd
+	//originPwdByte, err := base64.StdEncoding.DecodeString(req.OriginPassword)
+	//if err != nil {
+	//	br.Msg = "解析数据失败"
+	//	br.ErrMsg = "解析数据失败,Err:" + err.Error()
+	//	return
+	//}
+	//stringPwd := string(originPwdByte)
+	//if sysUser.Password != utils.MD5(stringPwd) {
+	//	br.Msg = "原密码错误"
+	//	return
+	//}
+	stringPwd := req.OriginPassword
+	if sysUser.Password != stringPwd {
+		br.Msg = "原密码错误"
+		return
 	}
+	//}
 
-	sysUser.Password = pwd
+	sysUser.Password = req.Password
 	sysUser.ModifyTime = time.Now()
 	err = sysUser.Update([]string{"password", "modify_time"})
 	if err != nil {

+ 18 - 0
controllers/user.go

@@ -705,3 +705,21 @@ func (u *UserController) ReadMessages() {
 		return
 	}
 }
+
+// Optional
+// @Title 用户在线状态
+// @Description 用户在线状态
+// @Param	request	body UserLoginReq true "type json string"
+// @Success 200 {object} models.LoginResp
+// @router /optional [post]
+func (this *UserController) Optional() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	br.Ret = 200
+	br.Msg = "用户token校验成功"
+	br.Success = true
+	return
+}

+ 31 - 15
controllers/video.go

@@ -8,17 +8,25 @@ import (
 	"eta/eta_mini_crm_ht/models/request"
 	"eta/eta_mini_crm_ht/models/response"
 	"eta/eta_mini_crm_ht/services"
-	"eta/eta_mini_crm_ht/services/elastic"
 	"eta/eta_mini_crm_ht/utils"
+	"eta/eta_mini_crm_ht/utils/elastic"
+	"eta/eta_mini_crm_ht/utils/oss"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"io"
 	"os"
 	"path"
 	"strconv"
+	"strings"
 	"time"
 )
 
+var (
+	videoType = map[string]int{
+		".mp4": 1,
+	}
+)
+
 type VideoController struct {
 	BaseAuthController
 }
@@ -51,11 +59,13 @@ func (this *VideoController) UploadVideo() {
 		return
 	}
 	ext := path.Ext(h.Filename)
-	if ext != ".mp4" {
+	lowCaseExt := strings.ToLower(ext)
+	if _, ok := videoType[lowCaseExt]; !ok {
 		br.Msg = "视频格式不正确"
 		br.ErrMsg = "视频上传失败,Err:" + err.Error()
 		return
 	}
+
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "ht/audio" + dateDir
 	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
@@ -76,12 +86,7 @@ func (this *VideoController) UploadVideo() {
 	duration, err := GetMP4Duration(f)
 	if err != nil {
 		br.Msg = "视频上传失败"
-		br.ErrMsg = "获取MP4时长失败,Err:" + err.Error()
-		return
-	}
-	if err != nil {
-		br.Msg = "音频上传失败"
-		br.ErrMsg = "解析音频时常失败,Err:" + err.Error()
+		br.ErrMsg = "获取视频时长失败,Err:" + err.Error()
 		return
 	}
 
@@ -94,7 +99,7 @@ func (this *VideoController) UploadVideo() {
 		err = os.Remove(fpath)
 		fmt.Sprintf("删除文件失败:%v", err)
 	}()
-	ossClient := services.NewOssClient()
+	ossClient := oss.NewOssClient()
 	if ossClient == nil {
 		br.Msg = "视频上传失败"
 		br.ErrMsg = "初始化OSS服务失败"
@@ -488,6 +493,11 @@ func (this *VideoController) UploadFile() {
 		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
 		return
 	}
+	if !utils.CheckImageFormat(h.Filename, f, h) {
+		br.Msg = "图片上传失败"
+		br.ErrMsg = "图片上传失败,不支持的文件格式"
+		return
+	}
 	defer f.Close()
 	size, err := strconv.Atoi(utils.UPLOAD_IMG_SIZE)
 	if err != nil {
@@ -499,11 +509,7 @@ func (this *VideoController) UploadFile() {
 		return
 	}
 	ext := path.Ext(h.Filename)
-	//if ext != ".mp3" {
-	//	br.Msg = "音频格式不正确"
-	//	br.ErrMsg = "音频上传失败,Err:" + err.Error()
-	//	return
-	//}
+
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "ht/audio" + dateDir
 	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
@@ -530,7 +536,7 @@ func (this *VideoController) UploadFile() {
 		err = os.Remove(fpath)
 		fmt.Sprintf("删除文件失败:%v", err)
 	}()
-	ossClient := services.NewOssClient()
+	ossClient := oss.NewOssClient()
 	if ossClient == nil {
 		br.Msg = "图片上传失败"
 		br.ErrMsg = "初始化OSS服务失败"
@@ -552,6 +558,16 @@ func (this *VideoController) UploadFile() {
 	br.Success = true
 }
 
+type Chunk struct {
+	Filename    string
+	UniqueCode  string
+	ChunkNumber int
+	TotalChunks int
+	Data        []byte
+}
+
+var chunksMap = make(map[string][]Chunk)
+
 // getFourccType 获取信息头类型
 func getFourccType(boxHeader BoxHeader) (fourccType string) {
 	return string(boxHeader.FourccType[:])

+ 9 - 5
go.mod

@@ -9,8 +9,11 @@ require (
 	github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
 	github.com/beego/bee/v2 v2.1.0
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
+	github.com/disintegration/imaging v1.6.2
+	github.com/gabriel-vasile/mimetype v1.4.7
 	github.com/go-redis/redis/v8 v8.11.5
 	github.com/go-sql-driver/mysql v1.8.1
+	github.com/google/uuid v1.6.0
 	github.com/minio/minio-go/v7 v7.0.74
 	github.com/olivere/elastic/v7 v7.0.32
 	github.com/rdlucklib/rdluck_tools v1.0.3
@@ -24,6 +27,7 @@ require (
 
 require (
 	filippo.io/edwards25519 v1.1.0 // indirect
+	github.com/abema/go-mp4 v1.3.0 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
@@ -32,7 +36,6 @@ require (
 	github.com/fatih/structs v1.1.0 // indirect
 	github.com/go-ini/ini v1.67.0 // indirect
 	github.com/goccy/go-json v0.10.3 // indirect
-	github.com/google/uuid v1.6.0 // indirect
 	github.com/hashicorp/golang-lru v0.5.4 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
@@ -63,10 +66,11 @@ require (
 	github.com/valyala/bytebufferpool v1.0.0 // indirect
 	github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect
 	github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect
-	golang.org/x/crypto v0.28.0 // indirect
-	golang.org/x/net v0.30.0 // indirect
-	golang.org/x/sys v0.26.0 // indirect
-	golang.org/x/text v0.19.0 // indirect
+	golang.org/x/crypto v0.29.0 // indirect
+	golang.org/x/image v0.23.0 // indirect
+	golang.org/x/net v0.31.0 // indirect
+	golang.org/x/sys v0.27.0 // indirect
+	golang.org/x/text v0.21.0 // indirect
 	golang.org/x/time v0.6.0 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240820151423-278611b39280 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect

+ 5 - 0
main.go

@@ -3,6 +3,7 @@ package main
 import (
 	"eta/eta_mini_crm_ht/controllers"
 	_ "eta/eta_mini_crm_ht/routers"
+	"eta/eta_mini_crm_ht/services"
 	"eta/eta_mini_crm_ht/utils"
 	"fmt"
 	"github.com/beego/beego/v2/core/logs"
@@ -23,6 +24,10 @@ func main() {
 	// 内存调整
 	web.BConfig.MaxMemory = 1024 * 1024 * 128
 	web.BConfig.RecoverFunc = Recover
+
+	if utils.MinioClient != nil {
+		services.RepairImageData()
+	}
 	web.Run()
 }
 

+ 3 - 0
models/financial_analyst.go

@@ -19,6 +19,7 @@ type CrmFinancialAnalyst struct {
 	HTId                    int           `description:"ht_id"`
 	Name                    string        `description:"姓名"`
 	HeadImgUrl              string        `description:"头像"`
+	HeadOriginImgUrl        string        `description:"原始头像"`
 	Position                string        `description:"职位"`
 	InvestmentCertificate   string        `description:"投资咨询资格证"`
 	ProfessionalCertificate string        `description:"从业资格证"`
@@ -37,6 +38,7 @@ type AnalystView struct {
 	Id                      int    `json:"Id"`
 	Name                    string `json:"Name"`
 	HeadImgURL              string `json:"HeadImgURL"`
+	HeadOriginImgUrl        string `json:"HeadOriginImgUrl"`
 	Position                string `json:"Position"`
 	InvestmentCertificate   string `json:"InvestmentCertificate"`
 	ProfessionalCertificate string `json:"ProfessionalCertificate"`
@@ -56,6 +58,7 @@ func (a *CrmFinancialAnalyst) ToView() *AnalystView {
 		Id:                      a.Id,
 		Name:                    a.Name,
 		HeadImgURL:              a.HeadImgUrl,
+		HeadOriginImgUrl:        a.HeadOriginImgUrl,
 		Position:                a.Position,
 		InvestmentCertificate:   a.InvestmentCertificate,
 		ProfessionalCertificate: a.ProfessionalCertificate,

+ 1 - 0
models/request/analyst.go

@@ -5,6 +5,7 @@ package request
 type AnalystEditReq struct {
 	Id                      int    `description:"用户id"`
 	HeadImgUrl              string `description:"头像地址"`
+	HeadOriginImgUrl        string `description:"原始头像地址"`
 	Introduction            string `description:"简介"`
 	Position                string
 	InvestmentCertificate   string

+ 7 - 0
models/response/analyst.go

@@ -9,3 +9,10 @@ type AnalystListResp struct {
 	List   []*models.AnalystView
 	Paging *paging.PagingItem `description:"分页数据"`
 }
+
+type HeadImageUploadResp struct {
+	Url            string `description:"缩略图URL"`
+	OriginUrl      string `description:"原始图片URL"`
+	FileName       string `description:""`
+	OriginFileName string `description:""`
+}

+ 9 - 0
routers/commentsRouter.go

@@ -711,6 +711,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mini_crm_ht/controllers:UserController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm_ht/controllers:UserController"],
+        beego.ControllerComments{
+            Method: "Optional",
+            Router: `/optional`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mini_crm_ht/controllers:UserController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm_ht/controllers:UserController"],
         beego.ControllerComments{
             Method: "ReadMessage",

+ 85 - 0
services/analyst.go

@@ -0,0 +1,85 @@
+package services
+
+import (
+	"eta/eta_mini_crm_ht/models"
+	"eta/eta_mini_crm_ht/utils"
+	"eta/eta_mini_crm_ht/utils/oss"
+	"fmt"
+	"os"
+	"sync"
+	"time"
+)
+
+func RepairImageData() {
+	sql := ` and (head_origin_img_url ='' or head_origin_img_url is null) `
+	count, err := models.GetAnalystCount(sql)
+	if err != nil {
+		fmt.Println("修复研究员头像数据失败")
+		return
+	}
+	list, err := models.GetAnalystList(sql, 0, count)
+	if err != nil {
+		fmt.Println("修复研究员头像数据失败")
+		return
+	}
+	ossClient := oss.NewOssClient()
+	if ossClient == nil {
+		fmt.Println("初始化OSS服务失败")
+		return
+	}
+	var wg sync.WaitGroup
+	wg.Add(len(list))
+	for _, v := range list {
+		go func(analyst *models.CrmFinancialAnalyst) {
+			defer wg.Done()
+			if analyst.HeadImgUrl == "" {
+				return
+			}
+			analyst.HeadOriginImgUrl = analyst.HeadImgUrl
+			dateDir := time.Now().Format(time.DateOnly)
+			downloadDir := utils.STATIC_DIR + "ht/analyst/repair/" + dateDir
+			if info, existErr := os.Stat(downloadDir); existErr != nil || !info.IsDir() {
+				err = os.MkdirAll(downloadDir, utils.DIR_MOD)
+				if err != nil {
+					fmt.Println("存储目录创建失败")
+					return
+				}
+			}
+			fileName := analyst.Name + "-头像"
+			savePath := downloadDir + "/" + fileName
+			savePath, err = ossClient.GetFile(analyst.HeadImgUrl, savePath)
+			if err != nil {
+				fmt.Printf("下载图片失败: %s,src:%s\n", err, analyst.HeadImgUrl)
+				return
+			}
+			var thumbnailPath string
+			thumbnailPath, err = utils.ImageResize(savePath, 400)
+			if err != nil {
+				fmt.Printf("图片压缩失败: %s,src:%s\n", err, analyst.HeadImgUrl)
+			}
+			if thumbnailPath != "" {
+				ossUploadDir := utils.RESOURCE_DIR + "analyst/"
+				saveOssPath := ossUploadDir + time.Now().Format("200601/20060102/")
+				imgName := utils.GetRandStringNoSpecialChar(28)
+				saveOssPath += imgName
+				var url string
+				url, err = ossClient.UploadFile("", thumbnailPath, saveOssPath)
+				if err != nil {
+					fmt.Printf("图片上传失败: %s,src:%s\n", err, analyst.HeadImgUrl)
+					return
+				}
+				analyst.HeadImgUrl = url
+				err = analyst.Update([]string{"head_img_url", "head_origin_img_url"})
+				if err != nil {
+					return
+				}
+			}
+			defer func() {
+				_ = os.Remove(savePath)
+				_ = os.Remove(thumbnailPath)
+			}()
+		}(v)
+	}
+	wg.Wait()
+	fmt.Println("修复研究员头像数据完成")
+}

+ 0 - 73
services/minio.go

@@ -1,73 +0,0 @@
-package services
-
-import (
-	"context"
-	"errors"
-	"eta/eta_mini_crm_ht/utils"
-	"fmt"
-	"log"
-	"time"
-
-	"github.com/minio/minio-go/v7"
-	"github.com/minio/minio-go/v7/pkg/credentials"
-)
-
-type MinioOss struct{}
-
-// UploadFile 上传文件
-func (m *MinioOss) UploadFile(fileName, filePath, savePath string) (string, error) {
-	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
-		return "0", errors.New("MinIo信息未配置")
-	}
-
-	ctx := context.Background()
-	// 此处兼容一下前后端endpoint不一致的情况, 前端用minio_endpoint后端用minio_back_endpoint, minio_back_endpoint为空则都取前者
-	endpoint := utils.MinIoEndpoint
-	if utils.MinIoBackEndpoint != "" {
-		endpoint = utils.MinIoBackEndpoint
-	}
-	accessKeyID := utils.MinIoAccessKeyId
-	secretAccessKey := utils.MinIoAccessKeySecret
-	useSSL := false
-	if utils.MinIoUseSSL == "true" {
-		useSSL = true
-	}
-	minioClient, err := minio.New(endpoint, &minio.Options{
-		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
-		Secure: useSSL,
-	})
-	if err != nil {
-		log.Fatalln(err)
-		return "1", err
-	}
-	bucketName := utils.MinIoBucketname
-	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
-	if errBucketExists != nil || !exists {
-		err = fmt.Errorf("BucketExists: %v; err: %v", exists, errBucketExists)
-		return "2", err
-	}
-
-	path := savePath
-	if savePath == "" {
-		path = utils.MinIoUploadDir + time.Now().Format("200601/20060102/") + fileName
-	}
-	_, err = minioClient.FPutObject(ctx, bucketName, path, filePath, minio.PutObjectOptions{})
-	if err != nil {
-		log.Fatalln(err)
-		return "3", err
-	}
-	resourceUrl := utils.MinIoPdfhost + path
-	return resourceUrl, err
-}
-
-func (m *MinioOss) GetUploadToken() (token OssToken, err error) {
-	token.AccessKeyId = utils.MinIoAccessKeyId
-	token.SecretKeyId = utils.MinIoAccessKeySecret
-	token.Endpoint = utils.MinIoEndpoint
-	token.ImgHost = utils.MinIoPdfhost
-	token.Bucketname = utils.MinIoBucketname
-	token.UseSSL = utils.MinIoUseSSL
-	token.RegionId = utils.MinIoRegion
-	token.Port = utils.MinIoPort
-	return
-}

+ 4 - 4
services/sso_service.go

@@ -1,18 +1,18 @@
 package services
 
 import (
-	"eta/eta_mini_crm_ht/rpc/sso"
+	sso2 "eta/eta_mini_crm_ht/utils/rpc/sso"
 )
 
 const (
 	crmSource = "weapp"
 )
 
-func SSOLogin(code string) (resp sso.UserResp, err error) {
-	req := &sso.LoginRequest{
+func SSOLogin(code string) (resp sso2.UserResp, err error) {
+	req := &sso2.LoginRequest{
 		Code:   code,
 		Source: crmSource,
 	}
-	ssoService := sso.GetSSOService()
+	ssoService := sso2.GetSSOService()
 	return ssoService.SSOLogin(req)
 }

+ 10 - 0
utils/config.go

@@ -15,6 +15,7 @@ var (
 	REDIS_CACHE string      //缓存地址
 	Rc          RedisClient //redis缓存
 	PRCPool     ClientPool  //rpc连接池
+	MinioClient *MinioOss
 )
 var ObjectStorageClient string // 目前有oss minio,默认oss
 // 推送模版消息
@@ -66,6 +67,8 @@ var (
 	UPLOAD_IMG_SIZE   string
 	UPLOAD_AUDIO_SIZE string
 	UPLOAD_VIDEO_SIZE string
+
+	UPLOAD_MULTI_VIDEO_SIZE string
 )
 
 // ES配置
@@ -238,6 +241,12 @@ func init() {
 	if UPLOAD_VIDEO_SIZE == "" {
 		UPLOAD_VIDEO_SIZE = "1000"
 	}
+
+	UPLOAD_MULTI_VIDEO_SIZE = config["upload_multi_video_size"]
+	if UPLOAD_MULTI_VIDEO_SIZE == "" {
+		UPLOAD_MULTI_VIDEO_SIZE = "1"
+	}
+
 	// 初始化缓存
 	redisClient, err := initRedis(config["redis_type"], config["beego_cache"])
 	if err != nil {
@@ -247,4 +256,5 @@ func init() {
 
 	Rc = redisClient
 
+	//MinioClient = DefaultClient()
 }

+ 1 - 0
utils/constants.go

@@ -79,6 +79,7 @@ var NoAuthApiMap = map[string]bool{
 	"/user/readMessages":      true,
 	"/user/message":           true,
 	"/sys/config":             true,
+	"/user/optional":          true,
 }
 
 var APPNAME string = "海通CRM"

+ 0 - 0
services/elastic/elastic.go → utils/elastic/elastic.go


+ 0 - 0
services/elastic/elasticsearch.go → utils/elastic/elasticsearch.go


+ 81 - 0
utils/imageUtils.go

@@ -0,0 +1,81 @@
+package utils
+
+import (
+	"github.com/disintegration/imaging"
+	"image"
+	"image/png"
+	"log"
+	"mime/multipart"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+var (
+	imgExtMap = map[string]string{
+		".jpg":  "image/jpeg",
+		".jpeg": "image/jpeg",
+		".png":  "image/png",
+	}
+)
+
+func CheckImageFormat(fileName string, file multipart.File, header *multipart.FileHeader) bool {
+	ext := strings.ToLower(filepath.Ext(fileName))
+	contentType := header.Header.Get("Content-Type")
+	if imgExtMap[ext] == contentType {
+		return true
+	}
+	return false
+}
+func ImageResize(imageSrc string, maxDimension int) (string, error) {
+	file, err := os.Open(imageSrc)
+	if err != nil {
+		log.Printf("打开图片失败: %v", err)
+		return "", err
+	}
+	defer file.Close()
+
+	// 解码图片
+	img, _, err := image.Decode(file)
+	if err != nil {
+		log.Printf("解码图片失败: %v", err)
+		return "", err
+	}
+
+	// 调整图片大小
+	resizedImg := imaging.Thumbnail(img, maxDimension, maxDimension, imaging.Lanczos)
+
+	// 获取输入文件的扩展名
+	ext := filepath.Ext(imageSrc)
+	ext = strings.ToLower(ext)
+
+	// 生成输出文件名
+	outputFileName := strings.TrimSuffix(imageSrc, ext) + "_resized" + ext
+	outFile, err := os.Create(outputFileName)
+	if err != nil {
+		log.Printf("创建输出文件失败: %v", err)
+		return "", err
+	}
+	defer outFile.Close()
+
+	// 根据文件扩展名选择编码格式
+	switch ext {
+	case ".jpg", ".jpeg":
+		err = imaging.Encode(outFile, resizedImg, imaging.JPEG, imaging.JPEGQuality(75))
+	case ".png":
+		err = imaging.Encode(outFile, resizedImg, imaging.PNG, imaging.PNGCompressionLevel(png.BestCompression))
+	//case ".gif":
+	//	err = imaging.Encode(outFile, resizedImg, imaging.GIF, imaging.GIFNumColors(DW))
+	default:
+		log.Printf("不支持的图片格式: %s", ext)
+		return "", err
+	}
+
+	if err != nil {
+		log.Printf("编码图片失败: %v", err)
+		return "", err
+	}
+
+	log.Println("图片压缩并保存成功:", outputFileName)
+	return outputFileName, nil
+}

+ 125 - 0
utils/minio.go

@@ -0,0 +1,125 @@
+package utils
+
+import (
+	"context"
+	"github.com/minio/minio-go/v7"
+	"github.com/minio/minio-go/v7/pkg/credentials"
+	"sync"
+	"time"
+)
+
+const (
+	DefaultExpiredTime = 30 * time.Minute
+)
+
+var (
+	once        sync.Once
+	minioClient *MinioOss
+)
+
+type MinioOss struct {
+	bucketName  string
+	ObjectName  string
+	minioClient *minio.Client
+	ExpiredTime time.Duration
+}
+
+func (mo *MinioOss) oss() *minio.Client {
+	return mo.minioClient
+}
+func (mo *MinioOss) GetObject(objectName string, filePath string) (err error) {
+	err = minioClient.oss().FGetObject(context.Background(), mo.GetBucketName(), objectName, filePath, minio.GetObjectOptions{})
+	return
+}
+func (mo *MinioOss) SaveObject(objectName string, filePath string) (err error) {
+	_, err = minioClient.oss().FPutObject(context.Background(), mo.GetBucketName(), objectName, filePath, minio.PutObjectOptions{})
+	return
+}
+func (mo *MinioOss) GetState(objectName string) (state minio.ObjectInfo, err error) {
+	state, err = minioClient.oss().StatObject(context.Background(), mo.GetBucketName(), objectName, minio.GetObjectOptions{})
+	return
+}
+func (mo *MinioOss) GetObjectExt(contentType string) (ext string) {
+	return getFileExtensionFromContentType(contentType)
+}
+func getFileExtensionFromContentType(contentType string) string {
+	switch contentType {
+	case "image/jpeg":
+		return ".jpg"
+	case "image/png":
+		return ".png"
+	case "application/pdf":
+		return ".pdf"
+	case "application/msword":
+		return ".doc"
+	case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
+		return ".docx"
+	case "application/vnd.ms-excel":
+		return ".xls"
+	case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
+		return ".xlsx"
+	case "text/plain":
+		return ".txt"
+	case "application/json":
+		return ".json"
+	case "application/octet-stream":
+		return ".bin"
+	default:
+		return ""
+	}
+}
+
+func DefaultClient() *MinioOss {
+	once.Do(func() {
+		if minioClient == nil {
+			// 此处兼容一下前后端endpoint不一致的情况, 前端用minio_endpoint后端用minio_back_endpoint, minio_back_endpoint为空则都取前者
+			endpoint := MinIoEndpoint
+			if MinIoBackEndpoint != "" {
+				endpoint = MinIoBackEndpoint
+			}
+			accessKeyID := MinIoAccessKeyId
+			secretAccessKey := MinIoAccessKeySecret
+			useSSL := false
+			if MinIoUseSSL == "true" {
+				useSSL = true
+			}
+			client, err := minio.New(endpoint, &minio.Options{
+				Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+				Secure: useSSL,
+			})
+			if err != nil {
+				FileLog.Error("初始化minio客户端失败: %s", err)
+			}
+			bucketName := MinIoBucketname
+			exists, errBucketExists := client.BucketExists(context.Background(), bucketName)
+			if errBucketExists != nil || !exists {
+				FileLog.Error("BucketExists: %v; err: %v", exists, errBucketExists)
+			}
+			minioClient = &MinioOss{
+				minioClient: client,
+				bucketName:  bucketName,
+				ExpiredTime: DefaultExpiredTime,
+			}
+		}
+	})
+	return minioClient
+}
+
+func (mo *MinioOss) GetBucketName() string {
+	return mo.bucketName
+}
+func (mo *MinioOss) GetExpiredTime() time.Duration {
+	return mo.ExpiredTime
+}
+
+// 生成预签名的PUT URL
+func (mo *MinioOss) GeneratePreSignedPutURLHandler(objectName string) (url string, err error) {
+	// 生成预签名的PUT URL
+	PreSignedURL, err := minioClient.oss().PresignedPutObject(context.Background(), minioClient.GetBucketName(), objectName, minioClient.GetExpiredTime())
+	if err != nil {
+		FileLog.Error("生成预签名的PUT URL失败: %s", err)
+		return
+	}
+	url = PreSignedURL.String()
+	return
+}

+ 191 - 0
utils/oss/minio.go

@@ -0,0 +1,191 @@
+package oss
+
+import (
+	"context"
+	"errors"
+	"eta/eta_mini_crm_ht/utils"
+	"fmt"
+	"github.com/minio/minio-go/v7"
+	"github.com/minio/minio-go/v7/pkg/credentials"
+	"time"
+)
+
+type MinioOss struct{}
+
+// UploadFile 上传文件
+func (m *MinioOss) UploadFile(fileName, filePath, savePath string) (string, error) {
+	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+		return "0", errors.New("MinIo信息未配置")
+	}
+
+	ctx := context.Background()
+	// 此处兼容一下前后端endpoint不一致的情况, 前端用minio_endpoint后端用minio_back_endpoint, minio_back_endpoint为空则都取前者
+	endpoint := utils.MinIoEndpoint
+	if utils.MinIoBackEndpoint != "" {
+		endpoint = utils.MinIoBackEndpoint
+	}
+	accessKeyID := utils.MinIoAccessKeyId
+	secretAccessKey := utils.MinIoAccessKeySecret
+	useSSL := false
+	if utils.MinIoUseSSL == "true" {
+		useSSL = true
+	}
+	minioClient, err := minio.New(endpoint, &minio.Options{
+		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+		Secure: useSSL,
+	})
+	if err != nil {
+		utils.FileLog.Error(err.Error())
+		return "1", err
+	}
+	bucketName := utils.MinIoBucketname
+	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+	if errBucketExists != nil || !exists {
+		err = fmt.Errorf("BucketExists: %v; err: %v", exists, errBucketExists)
+		return "2", err
+	}
+
+	path := savePath
+	if savePath == "" {
+		path = utils.MinIoUploadDir + time.Now().Format("200601/20060102/") + fileName
+	}
+	_, err = minioClient.FPutObject(ctx, bucketName, path, filePath, minio.PutObjectOptions{})
+	if err != nil {
+		utils.FileLog.Error(err.Error())
+		return "3", err
+	}
+	resourceUrl := utils.MinIoPdfhost + path
+	return resourceUrl, err
+}
+
+func (m *MinioOss) GetExt(string) (ext string, err error) {
+	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+		err = errors.New("MinIo信息未配置")
+		return
+	}
+	ctx := context.Background()
+	// 此处兼容一下前后端endpoint不一致的情况, 前端用minio_endpoint后端用minio_back_endpoint, minio_back_endpoint为空则都取前者
+	endpoint := utils.MinIoEndpoint
+	if utils.MinIoBackEndpoint != "" {
+		endpoint = utils.MinIoBackEndpoint
+	}
+	accessKeyID := utils.MinIoAccessKeyId
+	secretAccessKey := utils.MinIoAccessKeySecret
+	useSSL := false
+	if utils.MinIoUseSSL == "true" {
+		useSSL = true
+	}
+	minioClient, err := minio.New(endpoint, &minio.Options{
+		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+		Secure: useSSL,
+	})
+	if err != nil {
+		utils.FileLog.Error(err.Error())
+		return
+	}
+	bucketName := utils.MinIoBucketname
+	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+	if errBucketExists != nil || !exists {
+		err = fmt.Errorf("BucketExists: %v; err: %v", exists, errBucketExists)
+		return
+	}
+	info, minioErr := minioClient.StatObject(ctx, bucketName, "test.txt", minio.GetObjectOptions{})
+	if minioErr != nil {
+		err = fmt.Errorf("StatObject: %v; err: %v", info, minioErr)
+		return
+	}
+	ext = GetFileExtensionFromContentType(info.ContentType)
+	return
+}
+func (m *MinioOss) GetUploadToken() (token OssToken, err error) {
+	token.AccessKeyId = utils.MinIoAccessKeyId
+	token.SecretKeyId = utils.MinIoAccessKeySecret
+	token.Endpoint = utils.MinIoEndpoint
+	token.ImgHost = utils.MinIoPdfhost
+	token.Bucketname = utils.MinIoBucketname
+	token.UseSSL = utils.MinIoUseSSL
+	token.RegionId = utils.MinIoRegion
+	token.Port = utils.MinIoPort
+	return
+}
+
+func (m *MinioOss) GetFile(filePath, savePath string) (path string, err error) {
+	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+		err = errors.New("MinIo信息未配置")
+		return
+	}
+	ctx := context.Background()
+	// 此处兼容一下前后端endpoint不一致的情况, 前端用minio_endpoint后端用minio_back_endpoint, minio_back_endpoint为空则都取前者
+	endpoint := utils.MinIoEndpoint
+	if utils.MinIoBackEndpoint != "" {
+		endpoint = utils.MinIoBackEndpoint
+	}
+	accessKeyID := utils.MinIoAccessKeyId
+	secretAccessKey := utils.MinIoAccessKeySecret
+	useSSL := false
+	if utils.MinIoUseSSL == "true" {
+		useSSL = true
+	}
+	minioClient, err := minio.New(endpoint, &minio.Options{
+		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+		Secure: useSSL,
+	})
+	if err != nil {
+		utils.FileLog.Error(err.Error())
+		return
+	}
+	bucketName := utils.MinIoBucketname
+	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+	if errBucketExists != nil || !exists {
+		err = fmt.Errorf("BucketExists: %v; err: %v", exists, errBucketExists)
+		utils.FileLog.Error(err.Error())
+		return
+	}
+	ext, err := m.GetExt(filePath)
+	if err != nil {
+		err = fmt.Errorf("GetExt: %v; err: %v", ext, err)
+		utils.FileLog.Error(err.Error())
+		return
+	}
+	path = savePath + ext
+	err = minioClient.FGetObject(ctx, bucketName, filePath, path, minio.GetObjectOptions{})
+	if err != nil {
+		utils.FileLog.Error(err.Error())
+		return
+	}
+	return
+}
+func (m *MinioOss) MultiUploadFile() (err error) {
+	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+		err = errors.New("MinIo信息未配置")
+		return
+	}
+	ctx := context.Background()
+	// 此处兼容一下前后端endpoint不一致的情况, 前端用minio_endpoint后端用minio_back_endpoint, minio_back_endpoint为空则都取前者
+	endpoint := utils.MinIoEndpoint
+	if utils.MinIoBackEndpoint != "" {
+		endpoint = utils.MinIoBackEndpoint
+	}
+	accessKeyID := utils.MinIoAccessKeyId
+	secretAccessKey := utils.MinIoAccessKeySecret
+	useSSL := false
+	if utils.MinIoUseSSL == "true" {
+		useSSL = true
+	}
+	minioClient, err := minio.New(endpoint, &minio.Options{
+		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+		Secure: useSSL,
+	})
+	if err != nil {
+		utils.FileLog.Error(err.Error())
+		return
+	}
+	bucketName := utils.MinIoBucketname
+	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+	if errBucketExists != nil || !exists {
+		err = fmt.Errorf("BucketExists: %v; err: %v", exists, errBucketExists)
+		return
+	}
+
+	return
+}

+ 102 - 5
services/oss.go → utils/oss/oss.go

@@ -1,15 +1,17 @@
-package services
+package oss
 
 import (
 	"encoding/json"
 	"errors"
 	"eta/eta_mini_crm_ht/utils"
 	"fmt"
-	"time"
-
-	"github.com/aliyun/aliyun-oss-go-sdk/oss"
-
 	"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+	"github.com/gabriel-vasile/mimetype"
+	"io"
+	"os"
+	"strings"
+	"time"
 )
 
 type STSToken struct {
@@ -26,6 +28,9 @@ type STSToken struct {
 type OssClient interface {
 	UploadFile(string, string, string) (string, error)
 	GetUploadToken() (OssToken, error)
+	GetFile(filePath, savePath string) (path string, err error)
+	GetExt(string) (string, error)
+	MultiUploadFile() error
 }
 
 func NewOssClient() OssClient {
@@ -160,6 +165,27 @@ func NewSTSToken() (item *STSToken, err error) {
 
 type AliOss struct{}
 
+func (m *AliOss) GetExt(ossPath string) (ext string, err error) {
+	if utils.AccessKeyId == `` {
+		return "0", errors.New("阿里云信息未配置")
+	}
+	client, err := oss.New(utils.Endpoint, utils.AccessKeyId, utils.AccessKeySecret)
+	if err != nil {
+		return
+	}
+	bucket, err := client.Bucket(utils.Bucketname)
+	if err != nil {
+		return
+	}
+	objectKey := ossPath[strings.Index(ossPath, utils.RESOURCE_DIR):]
+	metaInfo, err := bucket.GetObjectMeta(objectKey)
+	if err != nil {
+		return
+	}
+	ext = GetFileExtensionFromContentType(metaInfo["Content-Type"][0])
+	return
+}
+
 // UploadFile 上传文件
 func (m *AliOss) UploadFile(fileName, filePath, savePath string) (string, error) {
 	if utils.AccessKeyId == `` {
@@ -202,3 +228,74 @@ func (m *AliOss) GetUploadToken() (token OssToken, err error) {
 	token.Imghost = stsToken.Imghost
 	return
 }
+func (m *AliOss) MultiUploadFile() (err error) {
+	return
+}
+func (m *AliOss) GetFile(filePath, savePath string) (path string, err error) {
+	if utils.AccessKeyId == `` {
+		return "", errors.New("阿里云信息未配置")
+	}
+	client, err := oss.New(utils.Endpoint, utils.AccessKeyId, utils.AccessKeySecret)
+	if err != nil {
+		return
+	}
+	bucket, err := client.Bucket(utils.Bucketname)
+	if err != nil {
+		return
+	}
+	objectKey := filePath[strings.Index(filePath, utils.RESOURCE_DIR):]
+	reader, err := bucket.GetObject(objectKey)
+	if err != nil {
+		return
+	}
+	buffer := make([]byte, 512)
+	n, err := reader.Read(buffer)
+	if err != nil && err != io.EOF {
+		return "", fmt.Errorf("Error reading object: %s", err)
+	}
+	mime := mimetype.Detect(buffer[:n])
+	fileExt := mime.Extension()
+	path = savePath + fileExt
+	defer reader.Close()
+	// 打开或创建本地文件
+	localFile, err := os.Create(path)
+	if err != nil {
+		utils.FileLog.Info("处理原始图片信息失败:Error create local dir: %s", err)
+		return
+	}
+	defer localFile.Close()
+	// 将io.ReadCloser中的数据复制到本地文件
+	_, err = localFile.Write(buffer[:n])
+	if err != nil {
+		utils.FileLog.Info("处理原始图片信息失败:Error writing to local file: %s", err)
+		return
+	}
+	_, err = io.Copy(localFile, reader)
+	return
+}
+func GetFileExtensionFromContentType(contentType string) string {
+	switch contentType {
+	case "image/jpeg":
+		return ".jpg"
+	case "image/png":
+		return ".png"
+	case "application/pdf":
+		return ".pdf"
+	case "application/msword":
+		return ".doc"
+	case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
+		return ".docx"
+	case "application/vnd.ms-excel":
+		return ".xls"
+	case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
+		return ".xlsx"
+	case "text/plain":
+		return ".txt"
+	case "application/json":
+		return ".json"
+	case "application/octet-stream":
+		return ".bin"
+	default:
+		return ""
+	}
+}

+ 0 - 0
rpc/rpc.go → utils/rpc/rpc.go


+ 0 - 0
rpc/sso/sso.pb.go → utils/rpc/sso/sso.pb.go


+ 0 - 0
rpc/sso/sso_grpc.pb.go → utils/rpc/sso/sso_grpc.pb.go


+ 1 - 1
rpc/sso/sso_rpc.go → utils/rpc/sso/sso_rpc.go

@@ -4,8 +4,8 @@ import (
 	"context"
 	"encoding/base64"
 	"errors"
-	"eta/eta_mini_crm_ht/rpc"
 	"eta/eta_mini_crm_ht/utils"
+	"eta/eta_mini_crm_ht/utils/rpc"
 	"fmt"
 	"google.golang.org/grpc/metadata"
 	"sync"