Browse Source

Merge branch 'bzq/dev' of eta_mini/eta_mini_crm into master

鲍自强 8 months ago
parent
commit
995102cc44

+ 1 - 0
.gitignore

@@ -7,3 +7,4 @@ go.sum
 scheduler/etalogs/
 scheduler/conf/
 *_test.go
+/test/

+ 2 - 3
controllers/base_auth.go

@@ -11,7 +11,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/beego/beego/v2/client/orm"
 	"github.com/beego/beego/v2/server/web"
 )
 
@@ -43,7 +42,7 @@ func (c *BaseAuthController) Prepare() {
 
 			session, err := models.GetSysSessionByToken(token)
 			if err != nil {
-				if err == orm.ErrNoRows {
+				if err.Error() == utils.ErrNoRow() {
 					c.JSON(models.BaseResponse{Ret: 408, Msg: "信息已变更,请重新登陆!", ErrMsg: "Token 信息已变更:Token: " + token}, false, false)
 					c.StopRun()
 					return
@@ -70,7 +69,7 @@ func (c *BaseAuthController) Prepare() {
 			}
 			sysUser, err := models.GetSysUserById(session.SysUserId)
 			if err != nil {
-				if err == orm.ErrNoRows {
+				if err.Error() == utils.ErrNoRow() {
 					c.JSON(models.BaseResponse{Ret: 408, Msg: "信息已变更,请重新登陆!", ErrMsg: "获取sysUser信息失败: " + strconv.Itoa(session.SysUserId)}, false, false)
 					c.StopRun()
 					return

+ 665 - 0
controllers/report_pdf.go

@@ -0,0 +1,665 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_mini_crm/models"
+	"eta/eta_mini_crm/models/request"
+	"eta/eta_mini_crm/models/response"
+	"eta/eta_mini_crm/services"
+	"eta/eta_mini_crm/services/elastic"
+	"eta/eta_mini_crm/utils"
+	"os"
+	"path"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+type ReportPdfController struct {
+	BaseAuthController
+}
+
+// Author
+// @Title 获取报告作者接口
+// @Description 获取报告作者
+// @Success 200 {object} models.ReportAuthorResp
+// @router /author [get]
+func (this *ReportPdfController) Author() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	items, err := models.GetReportAuthorList()
+	if err != nil {
+		br.Msg = "获取失败!"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = items
+}
+
+// Add
+// @Title 添加研报
+// @Description 添加研报
+// @Param	request	body request.ReportPdfAddReq true "type json string"
+// @Success 200 {object} models.ReportAuthorResp
+// @router /add [post]
+func (this *ReportPdfController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req request.ReportPdfAddReq
+	if err := json.Unmarshal(this.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ClassifyIdFirst <= 0 && req.ClassifyIdSecond <= 0 && req.ClassifyIdThird <= 0 {
+		br.Msg = "请选择研报所属分类"
+		return
+	}
+	if req.PdfName == "" {
+		br.Msg = "pdf名称为空"
+		return
+	}
+	var nameFirst, nameSecond, nameThird *models.ClassifyView
+	var err error
+	if req.ClassifyIdFirst > 0 {
+		nameFirst, err = models.GetClassifyById(req.ClassifyIdFirst)
+		if err != nil {
+			br.Msg = "添加失败"
+			br.ErrMsg = "一级类名获取失败,Err:" + err.Error()
+			return
+		}
+	} else {
+		br.Msg = "该分类不存在或已删除,请刷新重试"
+		return
+	}
+	if req.ClassifyIdSecond > 0 {
+		nameSecond, err = models.GetClassifyById(req.ClassifyIdSecond)
+		if err != nil {
+			br.Msg = "添加失败"
+			br.ErrMsg = "二级类名获取失败,Err:" + err.Error()
+			return
+		}
+	}
+	if req.ClassifyIdThird > 0 {
+		nameThird, err = models.GetClassifyById(req.ClassifyIdThird)
+		if err != nil {
+			br.Msg = "添加失败"
+			br.ErrMsg = "三级类名获取失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	pdf := &models.ReportPdf{
+		PdfUrl:            req.PdfUrl,
+		PdfName:           req.PdfName,
+		Title:             req.Title,
+		Author:            req.Author,
+		Abstract:          req.Abstract,
+		ClassifyIdFirst:   req.ClassifyIdFirst,
+		ClassifyNameFirst: nameFirst.ClassifyName,
+		PublishTime:       time.Now(),
+		ModifyTime:        time.Now(),
+		SysUserId:         this.SysUser.SysUserId,
+		SysRealName:       this.SysUser.SysRealName,
+		State:             utils.ReportStatusUp,
+	}
+	if nameSecond != nil {
+		pdf.ClassifyIdSecond = nameSecond.Id
+		pdf.ClassifyNameSecond = nameSecond.ClassifyName
+	}
+	if nameThird != nil {
+		pdf.ClassifyIdThird = nameThird.Id
+		pdf.ClassifyNameThird = nameThird.ClassifyName
+	}
+	insertId, err := pdf.Insert()
+	if err != nil {
+		br.Msg = "添加失败"
+		br.ErrMsg = "pdf研报新增失败,Err:" + err.Error()
+		return
+	}
+	pdf.ReportPdfId = int(insertId)
+	// 添加es
+	go func(reportPdf *models.ReportPdf) {
+		reportpdfView := reportPdf.ToView()
+		docId := strconv.Itoa(reportpdfView.ReportPdfId)
+		err = elastic.EsAddOrEditReportPdf(utils.MINI_REPORT_INDEX_NAME, docId, reportpdfView)
+		if err != nil {
+			utils.FileLog.Info("pdf研报es新增失败,Err:" + err.Error())
+			return
+		}
+		utils.FileLog.Info("pdf研报es新增成功, pdfId:" + docId)
+	}(pdf)
+
+	br.Msg = "添加成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// @Title 上传pdf研报
+// @Description 上传pdf研报
+// @Param   File   query   file  true       "文件"
+// @Success 200 {object} models.ReportAuthorResp
+// @router /uploadPdf [post]
+func (this *ReportPdfController) UploadPdf() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	f, h, err := this.GetFile("File")
+	if err != nil {
+		br.Msg = "获取资源信息失败"
+		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
+		return
+	}
+	defer f.Close()
+
+	ext := path.Ext(h.Filename)
+	if ext != ".pdf" {
+		br.Msg = "文件格式不正确"
+		return
+	}
+	size, err := strconv.Atoi(utils.UPLOAD_PDF_SIZE)
+	if err != nil {
+		size = 15
+	}
+	if h.Size > 1024*1024*int64(size) {
+		br.Msg = "文件大小不能超过15M"
+		return
+	}
+	dateDir := time.Now().Format("20060102")
+	uploadDir := utils.STATIC_DIR + "dongwu/" + dateDir
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
+	if err != nil {
+		br.Msg = "存储目录创建失败"
+		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
+		return
+	}
+	randStr := utils.GetRandStringNoSpecialChar(28)
+	fileName := randStr + ext
+	fpath := uploadDir + "/" + fileName
+	err = this.SaveToFile("File", fpath)
+	if err != nil {
+		br.Msg = "文件上传失败"
+		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+		return
+	}
+	pdfUploadDir := utils.RESOURCE_DIR + "pdf/"
+	savePdfToOssPath := pdfUploadDir + time.Now().Format("200601/20060102/")
+	pptName := utils.GetRandStringNoSpecialChar(28)
+	savePdfToOssPath += pptName + ".pdf"
+
+	defer func() {
+		_ = os.Remove(fpath)
+	}()
+
+	ossClient := services.NewOssClient()
+	if ossClient == nil {
+		br.Msg = "文件上传失败"
+		br.ErrMsg = "初始化OSS服务失败"
+		return
+	}
+	pdfUrl, err := ossClient.UploadFile("", fpath, savePdfToOssPath)
+	if err != nil {
+		br.Msg = "文件上传失败"
+		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+		return
+	}
+
+	base := path.Base(h.Filename)
+	resp := new(response.ReportPdfUploadResp)
+	resp.Url = pdfUrl
+	resp.FileName = base
+
+	br.Data = resp
+	br.Msg = "上传成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// List
+// @Title pdf研报列表
+// @Description pdf研报列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   ClassifyIds   query   string  true       "二级分类id,可多选用英文,隔开"
+// @Param   State   query   int  true       "研报状态, 1:已发布 2:未发布"
+// @Param   PublishStartDate   query   string  true       "发布开始时间"
+// @Param   PublishEndDate   query   string  true       "发布结束时间"
+// @Param   ModifyStartDate   query   string  true       "更新开始时间"
+// @Param   ModifyEndDate   query   string  true       "更新结束时间"
+// @Param   KeyWord   query   string  true       "报告标题/创建人"
+// @Param   SortParam   query   string  true       "排序字段"
+// @Param   SortType   query   string  true       "排序方式"
+// @Success 200 {object} models.ReportAuthorResp
+// @router /list [get]
+func (this *ReportPdfController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	classifyIds := this.GetString("ClassifyIds")
+	state, _ := this.GetInt("State")
+	publishStartDate := this.GetString("PublishStartDate")
+	publishEndDate := this.GetString("PublishEndDate")
+	modifyStartDate := this.GetString("ModifyStartDate")
+	modifyEndDate := this.GetString("ModifyEndDate")
+	keyWord := this.GetString("KeyWord")
+	sortParam := this.GetString("SortParam")
+	sortType := this.GetString("SortType")
+
+	var condition string
+	var pars []interface{}
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	if classifyIds != "" {
+		classifyArr := strings.Split(classifyIds, ",")
+		condition += " AND classify_id_second in (" + utils.GetOrmReplaceHolder(len(classifyArr)) + ")"
+		pars = append(pars, classifyArr)
+	}
+
+	switch state {
+	case utils.ReportStatusUp:
+		condition += " AND state = ?"
+		pars = append(pars, state)
+	case utils.ReportStatusDown:
+		condition += " AND state = ?"
+		pars = append(pars, state)
+	}
+
+	if publishStartDate != "" && publishEndDate != "" {
+		condition += " AND publish_time >= ?"
+		publishStartTime, err := time.Parse(utils.FormatDate, publishStartDate)
+		if err != nil {
+			br.Msg = "日期格式有误"
+			br.ErrMsg = "日期格式有误,Err:" + err.Error()
+			return
+		}
+		publishStartDateStr := publishStartTime.Format(utils.FormatDateTime)
+		pars = append(pars, publishStartDateStr)
+
+		condition += " AND publish_time <= ?"
+		publishEndTime, err := time.Parse(utils.FormatDate, publishEndDate)
+		if err != nil {
+			br.Msg = "日期格式有误"
+			br.ErrMsg = "日期格式有误,Err:" + err.Error()
+			return
+		}
+		publishEndTime = publishEndTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
+		publishEndDateStr := publishEndTime.Format(utils.FormatDateTime)
+		pars = append(pars, publishEndDateStr)
+	}
+	if modifyStartDate != "" && modifyEndDate != "" {
+		condition += " AND modify_time >= ?"
+		modifyStartTime, err := time.Parse(utils.FormatDate, modifyStartDate)
+		if err != nil {
+			br.Msg = "日期格式有误"
+			br.ErrMsg = "日期格式有误,Err:" + err.Error()
+			return
+		}
+		modifyStartDateStr := modifyStartTime.Format(utils.FormatDateTime)
+		pars = append(pars, modifyStartDateStr)
+
+		condition += " AND modify_time <= ?"
+		modifyEndTime, err := time.Parse(utils.FormatDate, modifyEndDate)
+		if err != nil {
+			br.Msg = "日期格式有误"
+			br.ErrMsg = "日期格式有误,Err:" + err.Error()
+			return
+		}
+		modifyEndTime = modifyEndTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
+		modifyEndDateStr := modifyEndTime.Format(utils.FormatDateTime)
+		pars = append(pars, modifyEndDateStr)
+	}
+	if keyWord != "" {
+		condition += ` AND (title like ? OR sys_real_name like ?) `
+		pars = utils.GetLikeKeywordPars(pars, keyWord, 2)
+	}
+	var sortCondition string
+	if sortParam != "" && sortType != "" {
+		sortCondition = " ORDER BY "
+		var param, sort string
+		switch sortParam {
+		case "PublishTime":
+			param = "publish_time"
+		case "ModifyTime":
+			param = "modify_time"
+		}
+		switch sortType {
+		case "asc":
+			sort = " ASC "
+		case "desc":
+			sort = " DESC "
+		}
+		if param != "" && sort != "" {
+			sortCondition += param + " " + sort
+		} else {
+			sortCondition = ""
+		}
+	}
+	if sortCondition == "" {
+		sortCondition = ` ORDER BY modify_time DESC `
+	}
+
+	total, err := models.GetReportPdfCountByCondition(condition, pars)
+	if err != nil {
+		br.Msg = "获取研报列表失败"
+		br.ErrMsg = "获取研报列表统计失败,Err:" + err.Error()
+		return
+	}
+
+	startSize := utils.StartIndex(currentIndex, pageSize)
+	reportList, err := models.GetReportPdfByCondition(condition, sortCondition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取研报列表失败"
+		br.ErrMsg = "获取研报列表失败,Err:" + err.Error()
+		return
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(response.ReportPdfListResp)
+	resp.List = reportList
+	resp.Paging = page
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = resp
+	br.Msg = "获取成功"
+}
+
+// Edit
+// @Title 编辑研报
+// @Description 编辑研报
+// @Param	request	body request.ReportPdfEditReq true "type json string"
+// @Success 200 {object} models.ReportAuthorResp
+// @router /edit [post]
+func (this *ReportPdfController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req request.ReportPdfEditReq
+	if err := json.Unmarshal(this.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,Err:" + err.Error()
+		return
+	}
+	if req.ClassifyIdFirst <= 0 {
+		br.Msg = "请选择研报所属的一级分类"
+		return
+	}
+	if req.ClassifyIdSecond <= 0 {
+		br.Msg = "请选择研报所属的二级分类"
+		return
+	}
+	if req.PdfUrl == "" {
+		br.Msg = "请上传研报文件"
+		return
+	}
+	if req.PdfName == "" {
+		br.Msg = "请填写研报名称"
+		return
+	}
+	reportPdf, err := models.GetReportPdfById(req.ReportPdfId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "研报不存在或已删除,请刷新页面"
+			return
+		}
+		br.Msg = "获取研报失败"
+		br.ErrMsg = "获取研报失败,系统错误,Err:" + err.Error()
+		return
+	}
+
+	nameFirst, err := models.GetClassifyById(req.ClassifyIdFirst)
+	if err != nil {
+		br.Msg = "文件编辑失败"
+		br.ErrMsg = "一级类名获取失败,Err:" + err.Error()
+		return
+	}
+	nameSecond, err := models.GetClassifyById(req.ClassifyIdSecond)
+	if err != nil {
+		br.Msg = "文件编辑失败"
+		br.ErrMsg = "二级类名获取失败,Err:" + err.Error()
+		return
+	}
+	if reportPdf.PdfName != req.PdfName || reportPdf.Title != req.Title || reportPdf.ClassifyIdFirst != req.ClassifyIdFirst || reportPdf.ClassifyIdSecond != req.ClassifyIdSecond || reportPdf.Author != req.Author || reportPdf.Abstract != req.Abstract || reportPdf.PdfUrl != req.PdfUrl {
+		reportPdf.Title = req.Title
+		reportPdf.PdfName = req.PdfName
+		reportPdf.ClassifyIdFirst = req.ClassifyIdFirst
+		reportPdf.ClassifyIdSecond = req.ClassifyIdSecond
+		reportPdf.ClassifyNameFirst = nameFirst.ClassifyName
+		reportPdf.ClassifyNameSecond = nameSecond.ClassifyName
+		reportPdf.Author = req.Author
+		reportPdf.Abstract = req.Abstract
+		reportPdf.PdfUrl = req.PdfUrl
+		reportPdf.ModifyTime = time.Now()
+		err = reportPdf.Update([]string{"pdf_name", "title", "classify_id_first", "classify_id_second", "classify_name_first", "classify_name_second", "author", "abstract", "pdf_url", "modify_time"})
+		if err != nil {
+			br.Msg = "文件更新失败"
+			br.ErrMsg = "文件更新失败,Err:" + err.Error()
+			return
+		}
+		// 编辑es
+		go func(reportPdf *models.ReportPdf) {
+			reportpdfView := reportPdf.ToView()
+			docId := strconv.Itoa(reportpdfView.ReportPdfId)
+			err = elastic.EsAddOrEditReportPdf(utils.MINI_REPORT_INDEX_NAME, docId, reportpdfView)
+			if err != nil {
+				utils.FileLog.Info("pdf研报es编辑失败,Err:" + err.Error())
+				return
+			}
+			utils.FileLog.Info("pdf研报es编辑成功, pdfId:" + docId)
+		}(reportPdf)
+	}
+	br.Msg = "研报编辑成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// Publish
+// @Title 发布研报
+// @Description 发布研报
+// @Param   ReportPdfId   query   string  true       "pdf研报id"
+// @Success 200 {object} models.BaseResponse
+// @router /publish [get]
+func (this *ReportPdfController) Publish() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	ReportPdfId, _ := this.GetInt("ReportPdfId")
+	reportPdf, err := models.GetReportPdfById(ReportPdfId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "研报不存在或已删除,请刷新页面"
+			return
+		}
+		br.Msg = "获取研报失败"
+		br.ErrMsg = "获取研报失败,系统错误,Err:" + err.Error()
+		return
+	}
+	if reportPdf.State == utils.ReportStatusUp {
+		br.Msg = "研报已发布"
+		return
+	}
+	reportPdf.State = utils.ReportStatusUp
+	reportPdf.PublishTime = time.Now()
+	err = reportPdf.Update([]string{"state", "publish_time"})
+	if err != nil {
+		br.Msg = "发布研报失败"
+		br.ErrMsg = "发布研报失败,系统错误,Err:" + err.Error()
+		return
+	}
+	// 修改es
+	go func(reportPdf *models.ReportPdf) {
+		reportpdfView := reportPdf.ToView()
+		docId := strconv.Itoa(reportpdfView.ReportPdfId)
+		err = elastic.EsAddOrEditReportPdf(utils.MINI_REPORT_INDEX_NAME, docId, reportpdfView)
+		if err != nil {
+			utils.FileLog.Info("pdf研报es发布失败,Err:" + err.Error())
+			return
+		}
+		utils.FileLog.Info("pdf研报es发布成功, pdfId:" + docId)
+	}(reportPdf)
+	br.Msg = "发布研报成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// PublishCancel
+// @Title 取消发布研报
+// @Description 取消发布研报
+// @Param   ReportPdfId   query   string  true       "pdf研报id"
+// @Success 200 {object} models.BaseResponse
+// @router /publishCancel [get]
+func (this *ReportPdfController) PublishCancel() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	ReportPdfId, _ := this.GetInt("ReportPdfId")
+	reportPdf, err := models.GetReportPdfById(ReportPdfId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "研报不存在或已删除,请刷新页面"
+			return
+		}
+		br.Msg = "获取研报失败"
+		br.ErrMsg = "获取研报失败,系统错误,Err:" + err.Error()
+		return
+	}
+	if reportPdf.State == utils.ReportStatusDown {
+		br.Msg = "研报已撤销"
+		return
+	}
+	reportPdf.State = utils.ReportStatusDown
+	err = reportPdf.Update([]string{"state"})
+	if err != nil {
+		br.Msg = "发布研报失败"
+		br.ErrMsg = "发布研报失败,系统错误,Err:" + err.Error()
+		return
+	}
+	// 修改es
+	go func(reportPdf *models.ReportPdf) {
+		reportpdfView := reportPdf.ToView()
+		docId := strconv.Itoa(reportpdfView.ReportPdfId)
+		err = elastic.EsAddOrEditReportPdf(utils.MINI_REPORT_INDEX_NAME, docId, reportpdfView)
+		if err != nil {
+			utils.FileLog.Info("pdf研报es取消发布失败,Err:" + err.Error())
+			return
+		}
+		utils.FileLog.Info("pdf研报es取消发布成功, pdfId:" + docId)
+	}(reportPdf)
+	br.Msg = "撤销研报成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// delete
+// @Title 删除研报
+// @Description 删除研报
+// @Param   ReportPdfId   query   string  true       "pdf研报id"
+// @Success 200 {object} models.BaseResponse
+// @router /delete [get]
+func (this *ReportPdfController) Delete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	ReportPdfId, _ := this.GetInt("ReportPdfId")
+	reportPdf, err := models.GetReportPdfById(ReportPdfId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "研报不存在或已删除,请刷新页面"
+			return
+		}
+		br.Msg = "获取研报失败"
+		br.ErrMsg = "获取研报失败,系统错误,Err:" + err.Error()
+		return
+	}
+	if reportPdf.State == utils.ReportStatusUp {
+		br.Msg = "研报已发布,不可以删除"
+		return
+	}
+
+	reportPdf.State = utils.ReportStatusDown
+	err = reportPdf.Delete()
+	if err != nil {
+		br.Msg = "研报删除失败"
+		br.ErrMsg = "研报删除失败,系统错误,Err:" + err.Error()
+		return
+	}
+	// 删除es
+	go func(reportPdf *models.ReportPdf) {
+		reportpdfView := reportPdf.ToView()
+		docId := strconv.Itoa(reportpdfView.ReportPdfId)
+		err = elastic.EsDeleteData(utils.MINI_REPORT_INDEX_NAME, docId)
+		if err != nil {
+			utils.FileLog.Info("pdf研报es删除失败,Err:" + err.Error())
+			return
+		}
+		utils.FileLog.Info("pdf研报es删除成功, pdfId:" + docId)
+	}(reportPdf)
+	br.Msg = "删除研报成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// Detail
+// @Title 研报详情
+// @Description 研报详情
+// @Param   ReportPdfId   query   string  true       "pdf研报id"
+// @Success 200 {object} models.BaseResponse
+// @router /detail [get]
+func (this *ReportPdfController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	ReportPdfId, _ := this.GetInt("ReportPdfId")
+	reportPdf, err := models.GetReportPdfById(ReportPdfId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "研报不存在或已删除,请刷新页面"
+			return
+		}
+		br.Msg = "获取研报失败"
+		br.ErrMsg = "获取研报失败,系统错误,Err:" + err.Error()
+		return
+	}
+
+	br.Data = reportPdf
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取研报成功"
+}

+ 2 - 2
controllers/sys_role.go

@@ -116,7 +116,7 @@ func (this *SysRoleController) Delete() {
 
 	role, err := models.GetSysRoleById(req.SysRoleId)
 	if err != nil {
-		if err == orm.ErrNoRows {
+		if err.Error() == utils.ErrNoRow() {
 			br.Msg = "角色不存在, 请刷新页面"
 			return
 		}
@@ -230,7 +230,7 @@ func (this *SysRoleController) Edit() {
 
 	sysRole, err := models.GetSysRoleById(req.SysRoleId)
 	if err != nil {
-		if err == orm.ErrNoRows {
+		if err.Error() == utils.ErrNoRow() {
 			br.Msg = "角色不存在,请刷新页面"
 			return
 		}

+ 5 - 5
controllers/sys_user.go

@@ -79,7 +79,7 @@ func (this *SysUserController) Add() {
 	}
 	_, err = models.GetSysDepartmentById(req.SysDepartmentId)
 	if err != nil {
-		if err == orm.ErrNoRows {
+		if err.Error() == utils.ErrNoRow() {
 			br.Msg = "所选部门不存在"
 			return
 		}
@@ -90,7 +90,7 @@ func (this *SysUserController) Add() {
 
 	sysRole, err := models.GetSysRoleById(req.SysRoleId)
 	if err != nil {
-		if err == orm.ErrNoRows {
+		if err.Error() == utils.ErrNoRow() {
 			br.Msg = "所选角色不存在"
 			return
 		}
@@ -233,7 +233,7 @@ func (this *SysUserController) Edit() {
 
 	sysUser, err := models.GetSysUserById(req.SysUserId)
 	if err != nil {
-		if err == orm.ErrNoRows {
+		if err.Error() == utils.ErrNoRow() {
 			br.Msg = "用户不存在,请刷新页面"
 			return
 		}
@@ -275,7 +275,7 @@ func (this *SysUserController) Edit() {
 	var roleName string
 	roleItem, err := models.GetSysRoleById(req.SysRoleId)
 	if err != nil {
-		if err == orm.ErrNoRows {
+		if err.Error() == utils.ErrNoRow() {
 			br.Msg = "角色不存在,请重新选择"
 			br.ErrMsg = "角色不存在"
 			return
@@ -452,7 +452,7 @@ func (this *SysUserController) ResetPass() {
 	}
 	sysUser, err := models.GetSysUserById(req.SysUserId)
 	if err != nil {
-		if err == orm.ErrNoRows {
+		if err.Error() == utils.ErrNoRow() {
 			br.Msg = "用户已被删除, 请刷新页面"
 			return
 		}

+ 96 - 3
controllers/user.go

@@ -284,7 +284,7 @@ func (this *UserController) Edit() {
 
 	user, err := models.GetUserById(req.UserId)
 	if err != nil {
-		if err == orm.ErrNoRows {
+		if err.Error() == utils.ErrNoRow() {
 			br.Msg = "用户不存在或已删除,请重新刷新页面"
 			br.ErrMsg = "用户不存在或已删除,请重新刷新页面,Err:" + err.Error()
 			return
@@ -924,7 +924,7 @@ func (this *UserController) PotentialEdit() {
 
 	user, err := models.GetUserById(req.UserId)
 	if err != nil {
-		if err == orm.ErrNoRows {
+		if err.Error() == utils.ErrNoRow() {
 			br.Msg = "用户不存在或已删除,请重新刷新页面"
 			br.ErrMsg = "用户不存在或已删除,请重新刷新页面,Err:" + err.Error()
 			return
@@ -992,7 +992,7 @@ func (this *UserController) Detail() {
 	}
 	user, err := models.GetUserViewById(UserId)
 	if err != nil {
-		if err == orm.ErrNoRows {
+		if err.Error() == utils.ErrNoRow() {
 			br.Msg = "用户不存在或已删除,请刷新页面"
 			return
 		}
@@ -1168,3 +1168,96 @@ func (this *UserController) ChangeList() {
 	br.Success = true
 	br.Ret = 200
 }
+
+// GlobalSearch
+// @Title 全局用户列表
+// @Description 全局用户列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "手机号/邮箱/姓名"
+// @Param   SortParam   query   string  true       "排序字段"
+// @Param   SortType   query   string  true       "排序方式"
+// @Success 200 {object} response.UserListResp
+// @router /global/list [get]
+func (this *UserController) GlobalSearch() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	keyWord := this.GetString("KeyWord")
+	sortParam := this.GetString("SortParam")
+	sortType := this.GetString("SortType")
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+
+	var condition string
+	var pars []interface{}
+	var sortCondition string
+
+	if keyWord == "" {
+		br.Ret = 200
+		br.Msg = "查询成功"
+		br.Success = true
+		return
+	} else {
+		condition = ` AND (real_name like ? OR phone like ? OR email like ?)`
+		pars = utils.GetLikeKeywordPars(pars, keyWord, 3)
+	}
+
+	if sortParam != "" && sortType != "" {
+		var param, sort string
+		switch sortParam {
+		case "RegisterTime":
+			param = "register_time"
+		case "ReadCnt":
+			param = "read_cnt"
+		case "LastUpdateTime":
+			param = "last_update_time"
+		}
+		switch sortType {
+		case "asc":
+			sort = "ASC"
+		case "desc":
+			sort = "DESC"
+		}
+		if param != "" && sort != "" {
+			sortCondition = ` ORDER BY ` + param + ` ` + sort
+		}
+	}
+	if sortCondition == "" {
+		sortCondition = ` ORDER BY u.register_time DESC`
+	}
+
+	startSize := utils.StartIndex(currentIndex, pageSize)
+	userIds, err := models.GetUserIdListByCondition(condition, pars)
+	if err != nil {
+		br.Msg = "查询失败"
+		br.ErrMsg = "查询失败,系统错误,Err:" + err.Error()
+		return
+	}
+
+	userList, err := models.GetGlobalUserByCondition(userIds, sortCondition, startSize, pageSize)
+	if err != nil {
+		br.Msg = "查询失败"
+		br.ErrMsg = "查询失败,系统错误,Err:" + err.Error()
+		return
+	}
+	page := paging.GetPaging(currentIndex, pageSize, len(userIds))
+	resp := new(response.UserListResp)
+	resp.List = userList
+	resp.Paging = page
+
+	br.Data = resp
+	br.Msg = "查询成功"
+	br.Success = true
+	br.Ret = 200
+}

+ 1 - 3
controllers/user_login.go

@@ -8,8 +8,6 @@ import (
 	"eta/eta_mini_crm/utils"
 	"fmt"
 	"time"
-
-	"github.com/beego/beego/v2/client/orm"
 )
 
 type UserLoginController struct {
@@ -46,7 +44,7 @@ func (this *UserLoginController) Login() {
 	}
 	sysUser, err := models.GetSysUserBySysUserName(req.UserName)
 	if err != nil {
-		if err == orm.ErrNoRows {
+		if err.Error() == utils.ErrNoRow() {
 			br.Msg = "登录失败, 账号或密码错误"
 			return
 		} else {

+ 22 - 18
controllers/user_read_record.go

@@ -9,7 +9,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/beego/beego/v2/client/orm"
 	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
@@ -53,7 +52,7 @@ func (this *UserReadRecordController) List() {
 	registerEndDate := this.GetString("RegisterEndDate")
 	createStartDate := this.GetString("CreateStartDate")
 	createEndDate := this.GetString("CreateEndDate")
-	sortParma := this.GetString("SortParam")
+	sortParam := this.GetString("SortParam")
 	sortType := this.GetString("SortType")
 
 	var condition string
@@ -90,10 +89,10 @@ func (this *UserReadRecordController) List() {
 			condition += `) `
 		}
 	}
-	if sortParma != "" && sortType != "" {
+	if sortParam != "" && sortType != "" {
 		sortCondition = " ORDER BY "
 		var param, sort string
-		switch sortParma {
+		switch sortParam {
 		case "LastUpdateTime":
 			param = "last_update_time"
 		case "ReadCnt":
@@ -251,7 +250,7 @@ func (this *UserReadRecordController) Detail() {
 	startSize := utils.StartIndex(currentIndex, pageSize)
 	user, err := models.GetUserById(UserId)
 	if err != nil {
-		if err == orm.ErrNoRows {
+		if err.Error() == utils.ErrNoRow() {
 			br.Msg = "用户不存在或已删除,请刷新页面"
 			return
 		}
@@ -282,30 +281,35 @@ func (this *UserReadRecordController) Detail() {
 			condition += `) `
 		}
 	}
+
+	var firstClassifyIds, secondClassifyIds, thirdClassifyIds []int
 	if classifyIds != "" {
 		ids := strings.Split(classifyIds, ",")
-		if len(ids) != 0 {
-			condition += ` AND ( `
-			for i, id := range ids {
-				if i == 0 {
-					condition += ` classify_id2 = ? `
-					pars = append(pars, id)
-				} else {
-					condition += ` OR classify_id2 = ? `
-					pars = append(pars, id)
-				}
+		classifyList, err := models.GetClassifyListByIds(ids)
+		if err != nil {
+			br.Msg = "查询失败"
+			br.Msg = "分类查询失败,系统错误,Err:" + err.Error()
+			return
+		}
+		for _, v := range classifyList {
+			switch v.Level {
+			case 1:
+				firstClassifyIds = append(firstClassifyIds, v.Id)
+			case 2:
+				secondClassifyIds = append(secondClassifyIds, v.Id)
+			case 3:
+				thirdClassifyIds = append(thirdClassifyIds, v.Id)
 			}
-			condition += `) `
 		}
 	}
 
-	total, err := models.GetUserReadRecordCountByUserId(UserId, condition, pars)
+	total, err := models.GetUserReadRecordCountByUserId(firstClassifyIds, secondClassifyIds, thirdClassifyIds, UserId, condition, pars)
 	if err != nil {
 		br.Msg = "查询阅读记录失败"
 		br.ErrMsg = "查询阅读记录失败,Err:" + err.Error()
 		return
 	}
-	readList, err := models.GetUserReadRecordByUserId(UserId, condition, pars, startSize, pageSize)
+	readList, err := models.GetUserReadRecordByUserId(firstClassifyIds, secondClassifyIds, thirdClassifyIds, UserId, condition, pars, startSize, pageSize)
 	if err != nil {
 		br.Msg = "查询阅读记录失败"
 		br.ErrMsg = "查询阅读记录失败,系统错误,Err:" + err.Error()

+ 25 - 6
go.mod

@@ -3,34 +3,53 @@ module eta/eta_mini_crm
 go 1.21
 
 require (
+	github.com/aliyun/alibaba-cloud-sdk-go v1.62.789
+	github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
 	github.com/beego/bee/v2 v2.1.0
 	github.com/beego/beego/v2 v2.1.0
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/go-redis/redis/v8 v8.11.5
 	github.com/go-sql-driver/mysql v1.7.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
-	github.com/robfig/cron/v3 v3.0.1
 )
 
 require (
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
+	github.com/dustin/go-humanize v1.0.1 // indirect
+	github.com/go-ini/ini v1.67.0 // indirect
+	github.com/goccy/go-json v0.10.3 // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/google/uuid v1.6.0 // indirect
 	github.com/hashicorp/golang-lru v0.5.4 // indirect
-	github.com/kr/text v0.2.0 // indirect
+	github.com/jmespath/go-jmespath v0.4.0 // indirect
+	github.com/josharian/intern v1.0.0 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/compress v1.17.9 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.8 // indirect
+	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
+	github.com/minio/md5-simd v1.1.2 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/prometheus/client_golang v1.15.1 // indirect
 	github.com/prometheus/client_model v0.3.0 // indirect
 	github.com/prometheus/common v0.42.0 // indirect
 	github.com/prometheus/procfs v0.9.0 // indirect
+	github.com/rs/xid v1.5.0 // indirect
 	github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
-	golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
-	golang.org/x/net v0.7.0 // indirect
-	golang.org/x/sys v0.6.0 // indirect
-	golang.org/x/text v0.7.0 // indirect
+	golang.org/x/crypto v0.24.0 // indirect
+	golang.org/x/net v0.26.0 // indirect
+	golang.org/x/sys v0.21.0 // indirect
+	golang.org/x/text v0.16.0 // indirect
+	golang.org/x/time v0.5.0 // indirect
 	google.golang.org/protobuf v1.30.0 // indirect
+	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )

+ 20 - 0
models/classify.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	"eta/eta_mini_crm/utils"
 	"time"
 
 	"github.com/beego/beego/v2/client/orm"
@@ -26,6 +27,8 @@ type ClassifyView struct {
 	ModifyTime    time.Time       `description:"修改时间"`
 	ClassifyLabel string          `description:"分类标签"`
 	Enabled       int             `description:"是否可用,1可用,0禁用"`
+	Level         int             `description:"分类层级"`
+	HasChild      int             `description:"是否有子分类0:下面没有子分类,1:下面有子分类"`
 	Child         []*ClassifyView `description:"子分类"`
 }
 
@@ -35,3 +38,20 @@ func GetClassifyList() (items []*ClassifyView, err error) {
 	_, err = o.Raw(sql).QueryRows(&items)
 	return
 }
+
+func GetClassifyById(classifyId int) (item *ClassifyView, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM classify WHERE enabled=1 AND id=?`
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+func GetClassifyListByIds(ids []string) (items []*ClassifyView, err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM classify WHERE enabled=1 AND id IN (` + utils.GetOrmReplaceHolder(len(ids)) + `)`
+	_, err = o.Raw(sql, ids).QueryRows(&items)
+	return
+}

+ 1 - 0
models/db.go

@@ -39,5 +39,6 @@ func init() {
 		new(SysMessageReport),
 		new(CrmConfig),
 		new(UserChangeRecord),
+		new(ReportPdf),
 	)
 }

+ 26 - 0
models/report_author.go

@@ -0,0 +1,26 @@
+package models
+
+import (
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type ReportAuthor struct {
+	Id           int       `orm:"column(id)" description:"报告作者ID"`
+	ReportAuthor string    `description:"报告作者名称"`
+	AuthorType   int       `description:"类型,1:中文;2:英文"`
+	Enable       int       `description:"是否启用,0:禁用,1:启用"`
+	IsDelete     int       `description:"是否删除,0:未删除,1:已删除"`
+	CreateTime   time.Time `description:"创建时间"`
+	ModifyTime   time.Time `description:"更新时间"`
+}
+
+// GetReportAuthorList 获取报告作者列表
+func GetReportAuthorList() (items []*ReportAuthor, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+
+	sql := ` SELECT * FROM report_author WHERE is_delete=0 AND enable=1 ORDER BY id desc  `
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}

+ 136 - 0
models/report_pdf.go

@@ -0,0 +1,136 @@
+package models
+
+import (
+	"eta/eta_mini_crm/utils"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type ReportPdf struct {
+	ReportPdfId        int       `orm:"pk" description:"id"`
+	PdfUrl             string    `description:"pdf文件URL"`
+	PdfName            string    `description:"pdf文件名称"`
+	Title              string    `description:"pdf文件标题"`
+	Author             string    `description:"作者"`
+	Abstract           string    `description:"摘要"`
+	ClassifyIdFirst    int       `description:"一级分类id"`
+	ClassifyNameFirst  string    `description:"一级分类名称"`
+	ClassifyIdSecond   int       `description:"二级分类id"`
+	ClassifyNameSecond string    `description:"二级分类名称"`
+	ClassifyIdThird    int       `description:"三级分类id"`
+	ClassifyNameThird  string    `description:"三级分类名称"`
+	Stage              int       `description:"期数"`
+	PublishTime        time.Time `description:"发布时间"`
+	ModifyTime         time.Time `description:"更新时间"`
+	Pv                 int       `description:"pv"`
+	Uv                 int       `description:"uv"`
+	SysUserId          int       `description:"创建人id"`
+	SysRealName        string    `description:"创建人姓名"`
+	State              int       `description:"状态"`
+}
+
+type ReportPdfView struct {
+	ReportPdfId        int       `orm:"pk" description:"id"`
+	PdfUrl             string    `description:"pdf文件URL"`
+	PdfName            string    `description:"pdf文件名称"`
+	Title              string    `description:"pdf文件标题"`
+	Author             string    `description:"作者"`
+	Abstract           string    `description:"摘要"`
+	ClassifyIdFirst    int       `description:"一级分类id"`
+	ClassifyNameFirst  string    `description:"一级分类名称"`
+	ClassifyIdSecond   int       `description:"二级分类id"`
+	ClassifyNameSecond string    `description:"二级分类名称"`
+	ClassifyIdThird    int       `description:"三级分类id"`
+	ClassifyNameThird  string    `description:"三级分类名称"`
+	Stage              int       `description:"期数"`
+	PublishTime        string    `description:"发布时间"`
+	ModifyTime         time.Time `description:"更新时间"`
+	Pv                 int       `description:"pv"`
+	Uv                 int       `description:"uv"`
+	SysUserId          int       `description:"创建人id"`
+	SysRealName        string    `description:"创建人姓名"`
+	State              int       `description:"状态"`
+}
+
+func (r *ReportPdf) Insert() (insertId int64, err error) {
+	o := orm.NewOrm()
+	// 计算研报期数
+	sql := `SELECT MAX(stage) + 1 AS count FROM report_pdf WHERE classify_id_second=?`
+	err = o.Raw(sql, r.ClassifyIdSecond).QueryRow(&r.Stage)
+	if r.Stage == 0 {
+		r.Stage = 1
+	}
+	if err != nil {
+		return
+	}
+	insertId, err = o.Insert(r)
+	return
+}
+
+func (r *ReportPdf) ToView() (item *ReportPdfView) {
+	item = new(ReportPdfView)
+	item.ReportPdfId = r.ReportPdfId
+	item.PdfUrl = r.PdfUrl
+	item.PdfName = r.PdfName
+	item.Title = r.Title
+	item.Author = r.Author
+	item.Abstract = r.Abstract
+	item.ClassifyIdFirst = r.ClassifyIdFirst
+	item.ClassifyNameFirst = r.ClassifyNameFirst
+	item.ClassifyIdSecond = r.ClassifyIdSecond
+	item.ClassifyNameSecond = r.ClassifyNameSecond
+	item.Stage = r.Stage
+	item.State = r.State
+	item.PublishTime = r.PublishTime.Format(utils.FormatDateTime)
+	item.ModifyTime = r.ModifyTime
+	item.Pv = r.Pv
+	item.Uv = r.Uv
+	item.SysUserId = r.SysUserId
+	item.SysRealName = r.SysRealName
+	return
+
+}
+
+func (r *ReportPdf) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(r, cols...)
+	return
+}
+
+func (r *ReportPdf) Delete() (err error) {
+	o := orm.NewOrm()
+	_, err = o.Delete(r)
+	return
+}
+
+func GetReportPdfCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT COUNT(*) AS count FROM report_pdf WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func GetReportPdfByCondition(condition, sortCondition string, pars []interface{}, startPage, pageSize int) (items []*ReportPdf, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM report_pdf WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	if sortCondition != "" {
+		sql += sortCondition // 排序
+	}
+	sql += ` LIMIT ?,?`
+	_, err = o.Raw(sql, pars, startPage, pageSize).QueryRows(&items)
+	return
+}
+
+func GetReportPdfById(id int) (item *ReportPdf, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM report_pdf WHERE report_pdf_id=?`
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}

+ 23 - 0
models/request/report_pdf.go

@@ -0,0 +1,23 @@
+package request
+
+type ReportPdfAddReq struct {
+	PdfUrl           string `description:"pdf文件URL"`
+	PdfName          string `description:"pdf文件名称"`
+	Title            string `description:"标题"`
+	Author           string `description:"作者"`
+	Abstract         string `description:"摘要"`
+	ClassifyIdFirst  int    `description:"一级分类id"`
+	ClassifyIdSecond int    `description:"二级分类id"`
+	ClassifyIdThird  int    `description:"三级分类id"`
+}
+
+type ReportPdfEditReq struct {
+	ReportPdfId      int    `description:"id"`
+	PdfName          string `description:"pdf文件名称"`
+	PdfUrl           string `description:"pdf文件URL"`
+	Title            string `description:"标题"`
+	Author           string `description:"作者"`
+	Abstract         string `description:"摘要"`
+	ClassifyIdFirst  int    `description:"一级分类id"`
+	ClassifyIdSecond int    `description:"二级分类id"`
+}

+ 17 - 0
models/response/report_pdf.go

@@ -0,0 +1,17 @@
+package response
+
+import (
+	"eta/eta_mini_crm/models"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+type ReportPdfListResp struct {
+	List   []*models.ReportPdf
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type ReportPdfUploadResp struct {
+	Url      string `description:"pdf文件URL"`
+	FileName string `description:"pdf文件名称"`
+}

+ 1 - 1
models/response/user_read_record.go

@@ -8,7 +8,7 @@ import (
 
 type UserReadRecordListResp struct {
 	Paging *paging.PagingItem
-	List   []*models.UserReadRecord
+	List   []*models.UserReadRecordView
 }
 
 type StaticInfoResp struct {

+ 16 - 0
models/user.go

@@ -2,6 +2,7 @@ package models
 
 import (
 	"context"
+	"eta/eta_mini_crm/utils"
 	"fmt"
 	"strings"
 	"time"
@@ -323,3 +324,18 @@ func DeleteUserById(userId int) (err error) {
 	})
 	return
 }
+
+func GetGlobalUserByCondition(userIds []int, sortCondition string, startSize, pageSize int) (items []*UserView, err error) {
+	if len(userIds) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `SELECT u.*, COUNT(ur.user_id) AS read_cnt, MAX(ur.create_time) AS last_update_time
+	FROM user AS u
+	LEFT JOIN user_read_record AS ur
+	ON u.user_id = ur.user_id 
+	WHERE u.user_id IN (` + utils.GetOrmReplaceHolder(len(userIds)) + `)
+	GROUP BY u.user_id ` + sortCondition + ` LIMIT ?,? `
+	_, err = o.Raw(sql, userIds, startSize, pageSize).QueryRows(&items)
+	return
+}

+ 59 - 5
models/user_read_record.go

@@ -1,6 +1,8 @@
 package models
 
 import (
+	"eta/eta_mini_crm/utils"
+	"fmt"
 	"time"
 
 	"github.com/beego/beego/v2/client/orm"
@@ -18,12 +20,38 @@ type UserReadRecord struct {
 	ClassifyName1       string    `description:"一级分类名称"`
 	ClassifyId2         int       `description:"二级分类id"`
 	ClassifyName2       string    `description:"二级分类名称"`
+	ClassifyId3         int       `description:"三级分类id"`
+	ClassifyName3       string    `description:"三级分类名称"`
 	Timestamp           int       `description:"阅读开始时间戳"`
 	EndTimestamp        int       `description:"阅读结束时间戳"`
 	CreateTime          time.Time `description:"创建时间"`
 	CreateDate          string    `description:"创建日期"`
 	StayTime            string    `description:"停留时间"`
 	StayTimestamp       string    `description:"停留时间戳"`
+	ReportType          int       `description:"报告类型:1-普通研报;2-pdf研报"`
+}
+
+type UserReadRecordView struct {
+	UserReadRecordId int    `orm:"pk" description:"id"`
+	UserId           int    `description:"用户id"`
+	ReportId         int    `description:"报告id"`
+	ReportTitle      string `description:"报告标题"`
+	// ChartPermissionId1  string    `description:"一级品种id"`
+	// ChartPermissionId2  string    `description:"二级品种id"`
+	ChartPermissionName string `description:"二级品种名称"`
+	ClassifyId1         int    `description:"一级级分类id"`
+	ClassifyName1       string `description:"一级分类名称"`
+	ClassifyId2         int    `description:"二级分类id"`
+	ClassifyName2       string `description:"二级分类名称"`
+	ClassifyId3         int    `description:"三级分类id"`
+	ClassifyName3       string `description:"三级分类名称"`
+	Timestamp           int    `description:"阅读开始时间戳"`
+	EndTimestamp        int    `description:"阅读结束时间戳"`
+	CreateTime          string `description:"创建时间"`
+	CreateDate          string `description:"创建日期"`
+	StayTime            string `description:"停留时间"`
+	StayTimestamp       string `description:"停留时间戳"`
+	ReportType          int    `description:"报告类型:1-普通研报;2-pdf研报"`
 }
 
 type ReadCntStaitc struct {
@@ -37,10 +65,10 @@ type PermissionCntStaitc struct {
 	Percent           float64
 }
 
-func GetUserReadRecordByUserId(userId int, condition string, pars []interface{}, startSize, pageSize int) (items []*UserReadRecord, err error) {
+func GetUserReadRecordByUserId(firstClassifyIds, secondClassifyIds, thirdClassifyIds []int, userId int, condition string, pars []interface{}, startSize, pageSize int) (items []*UserReadRecordView, err error) {
 	o := orm.NewOrm()
 	sql := `SELECT DISTINCT ur.user_read_record_id, ur.report_id, ur.report_title, ur.chart_permission_name, ur.classify_name2, 
-		ur.create_time, ur.stay_time, ur.classify_id2
+		ur.create_time, ur.stay_time, ur.classify_id2, ur.classify_id3, ur.classify_name3, ur.classify_id1, ur.classify_name1
 	  	FROM user_read_record AS ur
 		LEFT JOIN user_read_permission2 AS urp2
 		ON ur.user_read_record_id = urp2.user_read_record_id
@@ -48,12 +76,25 @@ func GetUserReadRecordByUserId(userId int, condition string, pars []interface{},
 	if condition != "" {
 		sql += condition
 	}
+	if len(firstClassifyIds) != 0 || len(secondClassifyIds) != 0 || len(thirdClassifyIds) != 0 {
+		sql += ` AND (1=2 `
+		if len(firstClassifyIds) > 0 {
+			sql += fmt.Sprintf(" OR ur.classify_id1 IN (%s) ", utils.GetOrmReplaceHolder(len(firstClassifyIds)))
+		}
+		if len(secondClassifyIds) > 0 {
+			sql += fmt.Sprintf(" OR ur.classify_id2 IN (%s) ", utils.GetOrmReplaceHolder(len(secondClassifyIds)))
+		}
+		if len(thirdClassifyIds) > 0 {
+			sql += fmt.Sprintf(" OR ur.classify_id3 IN (%s) ", utils.GetOrmReplaceHolder(len(thirdClassifyIds)))
+		}
+		sql += ` ) `
+	}
 	sql += ` ORDER BY create_time DESC LIMIT ?, ?`
-	_, err = o.Raw(sql, userId, pars, startSize, pageSize).QueryRows(&items)
+	_, err = o.Raw(sql, userId, pars, firstClassifyIds, secondClassifyIds, thirdClassifyIds, startSize, pageSize).QueryRows(&items)
 	return
 }
 
-func GetUserReadRecordCountByUserId(userId int, condition string, pars []interface{}) (count int, err error) {
+func GetUserReadRecordCountByUserId(firstClassifyIds, secondClassifyIds, thirdClassifyIds []int, userId int, condition string, pars []interface{}) (count int, err error) {
 	o := orm.NewOrm()
 	sql := `SELECT COUNT(DISTINCT ur.user_read_record_id) AS count
 	  	FROM user_read_record AS ur
@@ -63,7 +104,20 @@ func GetUserReadRecordCountByUserId(userId int, condition string, pars []interfa
 	if condition != "" {
 		sql += condition
 	}
-	err = o.Raw(sql, userId, pars).QueryRow(&count)
+	if len(firstClassifyIds) != 0 || len(secondClassifyIds) != 0 || len(thirdClassifyIds) != 0 {
+		sql += ` AND (1=2 `
+		if len(firstClassifyIds) > 0 {
+			sql += fmt.Sprintf(" OR ur.classify_id1 IN (%s) ", utils.GetOrmReplaceHolder(len(firstClassifyIds)))
+		}
+		if len(secondClassifyIds) > 0 {
+			sql += fmt.Sprintf(" OR ur.classify_id2 IN (%s) ", utils.GetOrmReplaceHolder(len(secondClassifyIds)))
+		}
+		if len(thirdClassifyIds) > 0 {
+			sql += fmt.Sprintf(" OR ur.classify_id3 IN (%s) ", utils.GetOrmReplaceHolder(len(thirdClassifyIds)))
+		}
+		sql += ` ) `
+	}
+	err = o.Raw(sql, userId, pars, firstClassifyIds, secondClassifyIds, thirdClassifyIds).QueryRow(&count)
 	return
 }
 

+ 90 - 0
routers/commentsRouter.go

@@ -25,6 +25,87 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"],
+        beego.ControllerComments{
+            Method: "Author",
+            Router: `/author`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"],
+        beego.ControllerComments{
+            Method: "Delete",
+            Router: `/delete`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"],
+        beego.ControllerComments{
+            Method: "Publish",
+            Router: `/publish`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"],
+        beego.ControllerComments{
+            Method: "PublishCancel",
+            Router: `/publishCancel`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:ReportPdfController"],
+        beego.ControllerComments{
+            Method: "UploadPdf",
+            Router: `/uploadPdf`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:SellerController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:SellerController"],
         beego.ControllerComments{
             Method: "List",
@@ -313,6 +394,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:UserController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:UserController"],
+        beego.ControllerComments{
+            Method: "GlobalSearch",
+            Router: `/global/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:UserController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm/controllers:UserController"],
         beego.ControllerComments{
             Method: "List",

+ 5 - 0
routers/router.go

@@ -62,6 +62,11 @@ func init() {
 				&controllers.UserReadRecordController{},
 			),
 		),
+		beego.NSNamespace("/report_pdf",
+			beego.NSInclude(
+				&controllers.ReportPdfController{},
+			),
+		),
 	)
 	beego.AddNamespace(ns)
 }

+ 5 - 3
scheduler/task.go

@@ -53,9 +53,11 @@ func ModifyUserStatus(ctx context.Context) (err error) {
 		record.CreateTime = time.Now()
 		userRecordList = append(userRecordList, record)
 	}
-	err = models.UserChangeRecordMultiInsert(userRecordList)
-	if err != nil {
-		return
+	if len(userRecordList) > 0 {
+		err = models.UserChangeRecordMultiInsert(userRecordList)
+		if err != nil {
+			return
+		}
 	}
 	return
 }

+ 171 - 0
services/elastic/elastic.go

@@ -0,0 +1,171 @@
+package elastic
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"eta/eta_mini_crm/models"
+	"eta/eta_mini_crm/utils"
+	"fmt"
+	"strconv"
+	"strings"
+
+	"github.com/olivere/elastic/v7"
+)
+
+// indexName:索引名称
+// mappingJson:表结构
+func EsCreateIndex(indexName, mappingJson string) (err error) {
+	client := utils.EsClient
+
+	//定义表结构
+	exists, err := client.IndexExists(indexName).Do(context.Background()) //<5>
+	if err != nil {
+		return
+	}
+	if !exists {
+		resp, err := client.CreateIndex(indexName).BodyJson(mappingJson).Do(context.Background())
+		//BodyJson(bodyJson).Do(context.Background())
+		if err != nil {
+			fmt.Println("CreateIndex Err:" + err.Error())
+			return err
+		}
+		fmt.Println(resp.Index, resp.ShardsAcknowledged, resp.Acknowledged)
+	} else {
+		fmt.Println(indexName + " 已存在")
+	}
+	return
+}
+
+// EsDeleteData 删除es中的指标数据
+func EsDeleteData(indexName, docId string) (err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("EsDeleteEdbInfoData Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	resp, err := client.Delete().Index(indexName).Id(docId).Do(context.Background())
+	fmt.Println(resp)
+	if err != nil {
+		return
+	}
+	if resp.Status == 0 {
+		fmt.Println("删除成功")
+	} else {
+		fmt.Println("AddData", resp.Status, resp.Result)
+	}
+	return
+}
+
+// EsAddOrEditReport 新增编辑es报告
+func EsAddOrEditReportPdf(indexName, docId string, item *models.ReportPdfView) (err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("EsAddOrEditReport Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	// docId为报告ID+章节ID
+	searchById, err := client.Get().Index(indexName).Id(docId).Do(context.Background())
+	if err != nil && !strings.Contains(err.Error(), "404") {
+		fmt.Println("Get Err" + err.Error())
+		return
+	}
+	if searchById != nil && searchById.Found {
+		resp, err := client.Update().Index(indexName).Id(docId).Doc(map[string]interface{}{
+			"ReportPdfId":        item.ReportPdfId,
+			"PdfUrl":             item.PdfUrl,
+			"PdfName":            item.PdfName,
+			"Title":              item.Title,
+			"Abstract":           item.Abstract,
+			"PublishTime":        item.PublishTime,
+			"ModifyTime":         item.ModifyTime,
+			"SysUserId":          item.SysUserId,
+			"SysRealName":        item.SysRealName,
+			"Author":             item.Author,
+			"State":              item.State,
+			"ClassifyIdFirst":    item.ClassifyIdFirst,
+			"ClassifyNameFirst":  item.ClassifyNameFirst,
+			"ClassifyIdSecond":   item.ClassifyIdSecond,
+			"ClassifyNameSecond": item.ClassifyNameSecond,
+			"ClassifyIdThird":    item.ClassifyIdThird,
+			"ClassifyNameThird":  item.ClassifyNameThird,
+			"Stage":              item.Stage,
+		}).Do(context.Background())
+		if err != nil {
+			return err
+		}
+		//fmt.Println(resp.Status, resp.Result)
+		if resp.Status == 0 {
+			fmt.Println("修改成功" + docId)
+			err = nil
+		} else {
+			fmt.Println("EditData", resp.Status, resp.Result)
+		}
+	} else {
+		resp, err := client.Index().Index(indexName).Id(docId).BodyJson(item).Do(context.Background())
+		if err != nil {
+			fmt.Println("新增失败:", err.Error())
+			return err
+		}
+		if resp.Status == 0 && resp.Result == "created" {
+			fmt.Println("新增成功" + docId)
+			return nil
+		} else {
+			fmt.Println("AddData", resp.Status, resp.Result)
+		}
+	}
+	return
+}
+
+// AnalyzeResp 分词接口返回结构体
+type AnalyzeResp struct {
+	Tokens []struct {
+		EndOffset   int64  `json:"end_offset"`
+		Position    int64  `json:"position"`
+		StartOffset int64  `json:"start_offset"`
+		Token       string `json:"token"`
+		Type        string `json:"type"`
+	} `json:"tokens"`
+}
+
+// Analyze 根据输入的文字获取分词后的文字
+func Analyze(content string) (contentList []string, err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("Analyze Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	queryMap := map[string]string{
+		"text":     content,
+		"analyzer": "ik_max_word",
+	}
+	res, err := client.PerformRequest(
+		context.Background(),
+		elastic.PerformRequestOptions{
+			Method: "GET",
+			Path:   "/_analyze",
+			Body:   queryMap,
+			Stream: false,
+		},
+	)
+	if res.StatusCode == 200 {
+		var analyzeResp AnalyzeResp
+		tmpErr := json.Unmarshal(res.Body, &analyzeResp)
+		if tmpErr != nil {
+			err = errors.New("返回数据转结构体失败:" + tmpErr.Error())
+			return
+		}
+		for _, v := range analyzeResp.Tokens {
+			contentList = append(contentList, v.Token)
+		}
+	} else {
+		err = errors.New("分词失败,返回code异常:" + strconv.Itoa(res.StatusCode))
+	}
+	return
+}

+ 12 - 0
services/elastic/elasticsearch.go

@@ -0,0 +1,12 @@
+package elastic
+
+import (
+	"fmt"
+)
+
+type tracelog struct{}
+
+// 实现输出
+func (tracelog) Printf(format string, v ...interface{}) {
+	fmt.Printf(format, v...)
+}

+ 73 - 0
services/minio.go

@@ -0,0 +1,73 @@
+package services
+
+import (
+	"context"
+	"errors"
+	"eta/eta_mini_crm/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
+}

+ 204 - 0
services/oss.go

@@ -0,0 +1,204 @@
+package services
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_mini_crm/utils"
+	"fmt"
+	"time"
+
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
+)
+
+type STSToken struct {
+	AccessKeyId     string
+	AccessKeySecret string
+	SecurityToken   string
+	ExpiredTime     string
+	RegionId        string
+	Bucketname      string
+	Endpoint        string
+	Imghost         string
+}
+
+type OssClient interface {
+	UploadFile(string, string, string) (string, error)
+	GetUploadToken() (OssToken, error)
+}
+
+func NewOssClient() OssClient {
+	switch utils.ObjectStorageClient {
+	case utils.STORAGESOURCE_OSS_NAME:
+		return new(AliOss)
+	default:
+		// 默认使用minio
+		return new(MinioOss)
+	}
+}
+
+// OssToken 此处为了兼容前端那边所以有重复的
+type OssToken struct {
+	AccessKeyId string
+	SecretKeyId string
+	RegionId    string
+	Bucketname  string
+	Endpoint    string
+	ImgHost     string
+	UseSSL      string
+	Port        string
+	//AccessKeyId     string
+	AccessKeySecret string
+	SecurityToken   string
+	ExpiredTime     string
+	//RegionId        string
+	//Bucketname      string
+	//Endpoint        string
+	Imghost      string
+	S3ForceStyle bool
+	S3Protocol   string
+}
+
+// GetOssSTSToken 获取STSToken
+func GetOssSTSToken() (item *STSToken, err error) {
+	defer func() {
+		if err != nil {
+			utils.FileLog.Info(err.Error())
+		}
+	}()
+	item = new(STSToken)
+	// 获取缓存中的Token
+	recent, _ := utils.Rc.RedisString(utils.STSTokenCacheKey)
+	if recent != "" {
+		lastToken := new(STSToken)
+		if e := json.Unmarshal([]byte(recent), &lastToken); e != nil {
+			err = errors.New("GetOssSTSToken lastToken Unmarshal Err: " + e.Error())
+			return
+		}
+		// 未防止正在上传大文件时Token过期, 将判定的过期时间提前10分钟
+		afterTime := time.Now().Local().Add(10 * time.Minute)
+		expired, e := time.ParseInLocation(utils.FormatDateTime, lastToken.ExpiredTime, time.Local)
+		if e != nil {
+			err = errors.New("GetOssSTSToken expiredTime Parse Err: " + e.Error())
+			return
+		}
+		if expired.After(afterTime) {
+			item.AccessKeyId = lastToken.AccessKeyId
+			item.AccessKeySecret = lastToken.AccessKeySecret
+			item.SecurityToken = lastToken.SecurityToken
+			item.ExpiredTime = lastToken.ExpiredTime
+			item.RegionId = utils.RegionId
+			item.Bucketname = utils.Bucketname
+			item.Endpoint = utils.Imghost
+			item.Imghost = utils.Imghost
+			return
+		}
+	}
+	// 已过期则获取新的token
+	newToken, e := NewSTSToken()
+	if e != nil {
+		err = errors.New("GetOssSTSToken NewSTSToken Err: " + e.Error())
+		return
+	}
+	newTokenJson, e := json.Marshal(newToken)
+	if e != nil {
+		err = errors.New("GetOssSTSToken NewToken JSON Err: " + e.Error())
+		return
+	}
+	// 覆盖缓存
+	if e := utils.Rc.Put(utils.STSTokenCacheKey, newTokenJson, time.Hour); e != nil {
+		err = errors.New("GetOssSTSToken SetRedis Err: " + e.Error())
+		return
+	}
+	item = newToken
+	return
+}
+
+// NewSTSToken 获取一个新的STSToken
+func NewSTSToken() (item *STSToken, err error) {
+	defer func() {
+		if err != nil {
+			utils.FileLog.Info(err.Error())
+		}
+	}()
+	item = new(STSToken)
+	client, e := sts.NewClientWithAccessKey("cn-shanghai", utils.RAMAccessKeyId, utils.RAMAccessKeySecret)
+	if e != nil {
+		err = errors.New("NewSTSToken NewClient Err: " + e.Error())
+		return
+	}
+	request := sts.CreateAssumeRoleRequest()
+	request.Scheme = utils.AliStsScheme
+	request.RegionId = utils.RegionId
+	request.RoleArn = utils.RoleArn
+	now := time.Now().Format(utils.FormatDateTimeUnSpace)
+	request.RoleSessionName = utils.RoleSessionName + now
+	request.DurationSeconds = "3600"
+	request.ConnectTimeout = 300 * time.Second
+	request.ReadTimeout = 300 * time.Second
+
+	response, e := client.AssumeRole(request)
+	if e != nil {
+		err = errors.New("NewSTSToken AssumeRole Err: " + e.Error())
+		return
+	}
+	if response != nil {
+		item.AccessKeyId = response.Credentials.AccessKeyId
+		item.AccessKeySecret = response.Credentials.AccessKeySecret
+		item.SecurityToken = response.Credentials.SecurityToken
+		t, _ := time.Parse(time.RFC3339, response.Credentials.Expiration)
+		expiration := t.In(time.Local)
+		item.ExpiredTime = expiration.Format(utils.FormatDateTime)
+		item.RegionId = utils.RegionId
+		item.Bucketname = utils.Bucketname
+		item.Endpoint = utils.Imghost
+		item.Imghost = utils.Imghost
+	}
+	return
+}
+
+type AliOss struct{}
+
+// UploadFile 上传文件
+func (m *AliOss) UploadFile(fileName, filePath, savePath string) (string, error) {
+	if utils.AccessKeyId == `` {
+		return "0", errors.New("阿里云信息未配置")
+	}
+	client, err := oss.New(utils.Endpoint, utils.AccessKeyId, utils.AccessKeySecret)
+	if err != nil {
+		return "1", err
+	}
+	bucket, err := client.Bucket(utils.Bucketname)
+	if err != nil {
+		return "2", err
+	}
+
+	path := savePath
+	if savePath == "" {
+		path = utils.UploadDir + time.Now().Format("200601/20060102/") + fileName
+	}
+	err = bucket.PutObjectFromFile(path, filePath)
+	if err != nil {
+		return "3", err
+	}
+	resourceUrl := utils.Imghost + path
+	return resourceUrl, err
+}
+
+func (m *AliOss) GetUploadToken() (token OssToken, err error) {
+	stsToken, e := GetOssSTSToken()
+	if e != nil {
+		err = fmt.Errorf("GetOssSTSToken err: %s", e.Error())
+		return
+	}
+	token.AccessKeyId = stsToken.AccessKeyId
+	token.AccessKeySecret = stsToken.AccessKeySecret
+	token.SecurityToken = stsToken.SecurityToken
+	token.ExpiredTime = stsToken.ExpiredTime
+	token.RegionId = stsToken.RegionId
+	token.Bucketname = stsToken.Bucketname
+	token.Endpoint = stsToken.Endpoint
+	token.Imghost = stsToken.Imghost
+	return
+}

+ 32 - 0
utils/common.go

@@ -3,10 +3,16 @@ package utils
 import (
 	"crypto/md5"
 	"encoding/hex"
+	"math/rand"
 	"regexp"
 	"strconv"
+	"strings"
+	"time"
 )
 
+// 随机种子
+var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
+
 func MD5(data string) string {
 	m := md5.Sum([]byte(data))
 	return hex.EncodeToString(m[:])
@@ -64,3 +70,29 @@ func StringsToJSON(str string) string {
 	}
 	return jsons
 }
+
+// 数据没有记录
+func ErrNoRow() string {
+	return "<QuerySeter> no row found"
+}
+
+func GetRandStringNoSpecialChar(size int) string {
+	allLetterDigit := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
+	randomSb := ""
+	digitSize := len(allLetterDigit)
+	for i := 0; i < size; i++ {
+		randomSb += allLetterDigit[rnd.Intn(digitSize)]
+	}
+	return randomSb
+}
+
+func GetOrmReplaceHolder(num int) string {
+	var stringBuffer strings.Builder
+	for i := 0; i < num; i++ {
+		stringBuffer.WriteString("?")
+		if i != num-1 {
+			stringBuffer.WriteString(",")
+		}
+	}
+	return stringBuffer.String()
+}

+ 126 - 0
utils/config.go

@@ -16,8 +16,65 @@ var (
 	REDIS_CACHE string      //缓存地址
 	Rc          RedisClient //redis缓存
 )
+var ObjectStorageClient string // 目前有oss minio,默认oss
 
+// 阿里云配置
 var (
+	Bucketname      string
+	Endpoint        string
+	Imghost         string
+	UploadDir       string
+	AccessKeyId     string
+	AccessKeySecret string
+)
+
+// MinIo配置
+var (
+	MinIoBucketname      string
+	MinIoEndpoint        string
+	MinIoBackEndpoint    string
+	MinIoPdfhost         string
+	MinIoUploadDir       string
+	MinIoAccessKeyId     string
+	MinIoAccessKeySecret string
+	MinIoUseSSL          string
+	MinIoPort            string
+	MinIoRegion          string
+)
+
+// 阿里云oss前端上传用
+var (
+	AliStsScheme       string
+	RegionId           string
+	RoleArn            string
+	RoleSessionName    string
+	RAMAccessKeyId     string
+	RAMAccessKeySecret string
+	STSTokenCacheKey   string
+)
+
+// 基础配置
+var (
+	STATIC_DIR      string
+	RESOURCE_DIR    string
+	UPLOAD_PDF_SIZE string // 单位MB
+)
+
+// ES配置
+var (
+	ES_URL      string // ES服务器地址
+	ES_USERNAME string // ES账号
+	ES_PASSWORD string // ES密码
+)
+
+// ES索引配置
+var (
+	MINI_REPORT_INDEX_NAME string // 小程序的pdf报告索引
+)
+
+var (
+	LogPath    string //调用过程中的日志存放地址
+	LogFile    string
 	ApiLogPath string // 接口请求地址和接口返回值日志存放地址
 	ApiLogFile string
 	BinLogPath string // 数据库相关的日志存放地址
@@ -60,6 +117,75 @@ func init() {
 	// 接口返回加密KEY
 	DesKey = config["des_key"]
 
+	// 对象存储客户端
+	ObjectStorageClient = config["object_storage_client"]
+	if ObjectStorageClient == "" {
+		ObjectStorageClient = "oss"
+	}
+
+	// OSS相关
+	{
+		Endpoint = config["endpoint"]
+		Bucketname = config["bucket_name"]
+		Imghost = config["img_host"]
+		UploadDir = config["upload_dir"]
+		AccessKeyId = config["access_key_id"]
+		AccessKeySecret = config["access_key_secret"]
+	}
+	// MinIo相关
+	{
+		MinIoEndpoint = config["minio_endpoint"]
+		MinIoBackEndpoint = config["minio_back_endpoint"]
+		MinIoBucketname = config["minio_bucket_name"]
+		MinIoPdfhost = config["minio_pdf_host"]
+		MinIoUploadDir = config["minio_upload_dir"]
+		MinIoAccessKeyId = config["minio_access_key_id"]
+		MinIoAccessKeySecret = config["minio_access_key_secret"]
+		MinIoUseSSL = config["minio_use_ssl"]
+		MinIoPort = config["minio_port"]
+		MinIoRegion = config["minio_region"]
+	}
+
+	// OSS相关(前端使用)
+	{
+		AliStsScheme = config["ali_sts_scheme"]
+		RegionId = config["region_id"]
+		RoleArn = config["role_arn"]
+		RoleSessionName = config["role_session_name"]
+		RAMAccessKeyId = config["ram_access_key_id"]
+		RAMAccessKeySecret = config["ram_access_key_secret"]
+		STSTokenCacheKey = config["sts_token_cache_key"]
+	}
+
+	// ES配置
+	{
+		ES_URL = config["es_url"]
+		ES_USERNAME = config["es_username"]
+		ES_PASSWORD = config["es_password"]
+	}
+
+	// ES 索引
+	{
+		MINI_REPORT_INDEX_NAME = config["mini_report_index_name"]
+	}
+
+	// 初始化ES
+	initEs()
+
+	// 静态文件目录
+	STATIC_DIR = config["static_dir"]
+	if STATIC_DIR == "" {
+		STATIC_DIR = "./static"
+	}
+	RESOURCE_DIR = config["resource_dir"]
+	if RESOURCE_DIR == "" {
+		RESOURCE_DIR = "dongwu/"
+	}
+	UPLOAD_PDF_SIZE = config["upload_pdf_size"]
+	if UPLOAD_PDF_SIZE == "" {
+		UPLOAD_PDF_SIZE = "15"
+	}
+
 	// 初始化缓存
 	redisClient, err := initRedis(config["redis_type"], config["beego_cache"])
 	if err != nil {

+ 16 - 0
utils/constants.go

@@ -1,5 +1,7 @@
 package utils
 
+import "io/fs"
+
 const (
 	UserLoginSalt = "MiQM9YUdf89T2uIH"         // 用户登录盐值
 	DesKeySalt    = "MxuqSoUrTAmyRd9fb0TtlrPk" // DesKey盐值
@@ -42,6 +44,9 @@ const (
 	MaxDepartmentLevel         = 3
 )
 
+// DIR_MOD 目录创建权限
+const DIR_MOD fs.FileMode = 0766 // Unix permission bits
+
 // 用户状态定义
 const (
 	UserStatusNo        = 0 //禁用
@@ -49,6 +54,17 @@ const (
 	UserStatusFormal    = 2 //正式用户
 )
 
+// Pdf研报状态定义
+const (
+	ReportStatusUp   = 1 // 研报已发布
+	ReportStatusDown = 2 // 研报未发布
+)
+
+const (
+	STORAGESOURCE_OSS_NAME   = "oss"
+	STORAGESOURCE_MINIO_NAME = "minio"
+)
+
 // 免验证接口
 var NoAuthApiMap = map[string]bool{
 	"/role/menu/buttons":      true,

+ 21 - 0
utils/elastic.go

@@ -0,0 +1,21 @@
+package utils
+
+import (
+	"github.com/olivere/elastic/v7"
+)
+
+// EsClient es客户端
+var EsClient *elastic.Client
+
+func initEs() {
+	client, err := elastic.NewClient(
+		elastic.SetURL(ES_URL),
+		elastic.SetBasicAuth(ES_USERNAME, ES_PASSWORD),
+		elastic.SetSniff(false))
+	EsClient = client
+	if err != nil {
+		panic("ElasticSearch连接失败,err:" + err.Error())
+		//go alarm_msg.SendAlarmMsg("ElasticSearch连接失败", 2)
+	}
+	return
+}

+ 27 - 2
utils/logs.go

@@ -11,14 +11,39 @@ import (
 const (
 	DefaultBinlogPath = "./etalogs/binlog"
 	DefaultApiLogPath = "./etalogs/apilog"
+	DefaultLogPath    = "./etalogs/filelog"
 )
 
 var (
-	BinLog *logs.BeeLogger
-	ApiLog *logs.BeeLogger
+	BinLog  *logs.BeeLogger
+	ApiLog  *logs.BeeLogger
+	FileLog *logs.BeeLogger
 )
 
 func init() {
+	if LogMaxDays == 0 {
+		LogMaxDays = 30
+	}
+	logPath := LogPath
+	if logPath == "" {
+		logPath = DefaultLogPath
+	}
+	logFile := LogFile
+	if logFile == "" {
+		logFile = "filelog.log"
+	}
+	os.MkdirAll(logPath, os.ModePerm)
+
+	// 打开文件
+	logFileName := path.Join(logPath, logFile)
+	FileLog = logs.NewLogger(1000000)
+	logConf := getDefaultLogConfig()
+
+	logConf.FileName = logFileName
+	b, _ := json.Marshal(logConf)
+	FileLog.SetLogger(logs.AdapterFile, string(b))
+	FileLog.EnableFuncCallDepth(true)
+
 	initApiLog()
 	initBinLog()
 }