hsun il y a 1 an
Parent
commit
86f8ce0225

+ 2984 - 0
controllers/report.go

@@ -0,0 +1,2984 @@
+package controllers
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/beego/beego/v2/server/web"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"hongze/hongze_ETA_mobile_api/models"
+	"hongze/hongze_ETA_mobile_api/services"
+	"hongze/hongze_ETA_mobile_api/services/alarm_msg"
+	"hongze/hongze_ETA_mobile_api/utils"
+	"html"
+	"os"
+	"path"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// ReportController 报告
+type ReportController struct {
+	BaseAuthController
+}
+
+// 报告
+//type ReportCommonController struct {
+//	BaseCommonController
+//}
+
+// ReportUploadCommonController 报告上传
+type ReportUploadCommonController struct {
+	web.Controller
+}
+
+// @Title 获取报告列表接口
+// @Description 获取报告列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   StartDate   query   string  true       "开始时间"
+// @Param   EndDate   query   string  true       "结束时间"
+// @Param   Frequency   query   string  true       "频度"
+// @Param   ClassifyNameFirst   query   string  true       "一级分类名称"
+// @Param   ClassifyNameSecond   query   string  true       "二级分类名称"
+// @Param   State   query   int  true       "状态"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Param   PublishSort   query   string  true       "desc:降序,asc 升序(预留)"
+// @Param   CompanyType   query   string  false       "产品类型,枚举值:'ficc','权益';不传默认返回全部"
+// @Success 200 {object} models.ReportListResp
+// @router /list [get]
+func (this *ReportController) ListReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+
+	startDate := this.GetString("StartDate")
+	endDate := this.GetString("EndDate")
+	frequency := this.GetString("Frequency")
+	classifyNameFirst := this.GetString("ClassifyNameFirst")
+	classifyNameSecond := this.GetString("ClassifyNameSecond")
+	state, _ := this.GetInt("State")
+	keyWord := this.GetString("KeyWord")
+	companyType := this.GetString("CompanyType")
+	msgIsSend, _ := this.GetInt("MsgIsSend")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	var condition string
+	var pars []interface{}
+
+	if keyWord != "" {
+		condition += ` AND (title LIKE '%` + keyWord + `%' OR author LIKE '%` + keyWord + `%' ) `
+	}
+	if startDate != "" {
+		condition += ` AND create_time >= ? `
+		pars = append(pars, startDate)
+	}
+	if endDate != "" {
+		condition += ` AND create_time <= ? `
+		pars = append(pars, endDate)
+	}
+	if frequency != "" {
+		condition += ` AND frequency = ? `
+		pars = append(pars, frequency)
+	}
+	if classifyNameFirst != "" {
+		condition += ` AND classify_name_first = ? `
+		pars = append(pars, classifyNameFirst)
+	}
+
+	if classifyNameSecond != "" {
+		condition += ` AND classify_name_second = ? `
+		pars = append(pars, classifyNameSecond)
+	}
+	if state > 0 {
+		condition += ` AND state = ? `
+		pars = append(pars, state)
+	}
+	// 消息是否已推送 1-未推送; 2-已推送
+	if msgIsSend == 1 {
+		condition += ` AND (msg_is_send = 0 OR ths_msg_is_send = 0) `
+	}
+	if msgIsSend == 2 {
+		condition += ` AND msg_is_send = 1 AND ths_msg_is_send = 1 `
+	}
+	total, err := models.GetReportListCount(condition, pars, companyType)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	list, err := models.GetReportList(condition, pars, companyType, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	listLen := len(list)
+	if listLen > 0 {
+		reportIdArr := make([]string, 0)
+		syncReportIdArr := make([]string, 0) // 同步过来的报告IDs
+		for i := 0; i < listLen; i++ {
+			reportIdArr = append(reportIdArr, strconv.Itoa(list[i].Id))
+			if list[i].OldReportId > 0 {
+				syncReportIdArr = append(syncReportIdArr, strconv.Itoa(list[i].OldReportId))
+			}
+		}
+		reportIds := strings.Join(reportIdArr, ",")
+		syncReportIds := strings.Join(syncReportIdArr, ",")
+
+		// 查询同步过来的报告对应的老报告PV+UV
+		pvMap := make(map[int]int, 0)
+		uvMap := make(map[int]int, 0)
+		if syncReportIds != "" {
+			puvList, e := models.GetPUVByResearchReportIds(syncReportIds)
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取同步报告对应的PV、UV失败, Err: " + e.Error()
+				return
+			}
+			puvLen := len(puvList)
+			for i := 0; i < puvLen; i++ {
+				pvMap[puvList[i].ResearchReportId] = puvList[i].Pv
+				uvMap[puvList[i].ResearchReportId] = puvList[i].Uv
+			}
+		}
+		// 晨周报音频列表
+		videoList, err := models.GetReportChapterVideoListByReportIds(reportIds)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取报告音频文件失败,Err:" + err.Error()
+			return
+		}
+		for i := 0; i < listLen; i++ {
+			list[i].Content = html.UnescapeString(list[i].Content)
+			list[i].ContentSub = html.UnescapeString(list[i].ContentSub)
+			// 除周报外其余报告均可推送客群
+			list[i].NeedThsMsg = 1
+			//if list[i].HasChapter == 1 && list[i].ChapterType == utils.REPORT_TYPE_WEEK {
+			//	list[i].NeedThsMsg = 0
+			//}
+			chapterList := make([]*models.ReportChapterVideoList, 0)
+			for ii := 0; ii < len(videoList); ii++ {
+				if list[i].Id == videoList[ii].ReportId {
+					chapterList = append(chapterList, videoList[ii])
+				}
+			}
+			list[i].ChapterVideoList = chapterList
+			list[i].Pv += pvMap[list[i].OldReportId]
+			list[i].Uv += uvMap[list[i].OldReportId]
+		}
+	}
+
+	for _, item := range list {
+		/*key := fmt.Sprint(`crm:report:edit:`, item.Id)
+		opUserId, _ := utils.Rc.RedisInt(key)
+		//如果当前没有人操作,获取当前操作人是本人,那么编辑按钮可用
+		if opUserId <= 0 || (opUserId == this.SysUser.AdminId) || item.ClassifyNameFirst == "周报" || item.ClassifyNameFirst == "晨报" {
+			item.CanEdit = true
+		} else {
+			adminInfo, errAdmin := system.GetSysUserById(opUserId)
+			if errAdmin != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取失败,Err:" + errAdmin.Error()
+				return
+			}
+			item.Editor = adminInfo.RealName
+		}*/
+		if item.ClassifyNameFirst == "周报" || item.ClassifyNameFirst == "晨报" {
+			item.CanEdit = true
+		} else {
+			markStatus, err := services.UpdateReportEditMark(item.Id, this.SysUser.AdminId, 2, this.SysUser.RealName)
+			if err != nil {
+				br.Msg = "查询标记状态失败"
+				br.ErrMsg = "查询标记状态失败,Err:" + err.Error()
+				return
+			}
+			if markStatus.Status == 0 {
+				item.CanEdit = true
+			} else {
+				item.Editor = markStatus.Editor
+			}
+		}
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(models.ReportListResp)
+	resp.Paging = page
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 发布报告接口
+// @Description 发布报告
+// @Param	request	body models.PublishReq true "type json string"
+// @Success 200 Ret=200 发布成功
+// @router /publish [post]
+func (this *ReportController) PublishReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.PublishReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	reportIds := req.ReportIds
+	if reportIds == "" {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,报告id不可为空"
+		return
+	}
+
+	reportArr := strings.Split(reportIds, ",")
+	tips := ""
+	for _, v := range reportArr {
+		vint, err := strconv.Atoi(v)
+		if err != nil {
+			br.Msg = "参数错误"
+			br.ErrMsg = "参数错误,Err:" + err.Error()
+			return
+		}
+		report, err := models.GetReportById(vint)
+		if err != nil {
+			br.Msg = "获取报告信息失败"
+			br.ErrMsg = "获取报告信息失败,Err:" + err.Error()
+			return
+		}
+		if report == nil {
+			br.Msg = "报告不存在"
+			return
+		}
+		var publishTime time.Time
+		if report.MsgIsSend == 1 && report.PublishTime != "" { //如果报告曾经发布过,并且已经发送过模版消息,则章节的发布时间为报告的发布时间
+			publishTime, _ = time.ParseInLocation(utils.FormatDateTime, report.PublishTime, time.Local)
+		} else {
+			publishTime = time.Now()
+		}
+
+		var tmpErr error
+		if report.HasChapter == 1 && (report.ChapterType == utils.REPORT_TYPE_DAY || report.ChapterType == utils.REPORT_TYPE_WEEK) {
+			// 发布晨周报
+			if tips, tmpErr = services.PublishDayWeekReport(vint); tmpErr != nil {
+				br.Msg = "报告发布失败"
+				br.ErrMsg = "晨周报发布失败, Err:" + tmpErr.Error() + ", report_id:" + strconv.Itoa(report.Id)
+				return
+			}
+		} else {
+			if report.Content == "" {
+				br.Msg = "报告内容为空,不可发布"
+				br.ErrMsg = "报告内容为空,不需要生成,report_id:" + strconv.Itoa(report.Id)
+				return
+			}
+			if tmpErr = models.PublishReportById(report.Id, publishTime); tmpErr != nil {
+				br.Msg = "报告发布失败"
+				br.ErrMsg = "报告发布失败, Err:" + tmpErr.Error() + ", report_id:" + strconv.Itoa(report.Id)
+				return
+			}
+			go func() {
+				// 生成音频
+				if report.VideoUrl == "" {
+					_ = services.CreateVideo(report)
+				}
+				//// 推送找钢网
+				//if utils.RunMode == "release" && (report.ClassifyNameSecond == "知白守黑日评" || report.ClassifyNameSecond == "股债日评") {
+				//	_ = services.ZhaoGangSend(report)
+				//}
+				// 更新报告Es
+				_ = services.UpdateReportEs(report.Id, 2)
+			}()
+		}
+	}
+	// 发布晨周报部分章节未发布的提示
+	if tips != "" {
+		br.Data = tips
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "发布成功"
+}
+
+// @Title 取消发布报告接口
+// @Description 取消发布报告
+// @Param	request	body models.PublishCancelReq true "type json string"
+// @Success 200 Ret=200 取消发布成功
+// @router /publish/cancle [post]
+func (this *ReportController) PublishCancleReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.PublishCancelReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ReportIds <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,报告id不可为空"
+		return
+	}
+	publishTimeNullFlag := true
+	reportInfo, err := models.GetReportById(req.ReportIds)
+	if err != nil {
+		br.Msg = "获取报告信息失败"
+		br.ErrMsg = "获取报告信息失败,Err:" + err.Error()
+		return
+	}
+	if reportInfo.MsgIsSend == 1 {
+		publishTimeNullFlag = false
+	}
+	err = models.PublishCancleReport(req.ReportIds, publishTimeNullFlag)
+	if err != nil {
+		br.Msg = "取消发布失败"
+		br.ErrMsg = "取消发布失败,Err:" + err.Error()
+		return
+	}
+	// 更新ES禁用
+	{
+		go services.UpdateReportEs(req.ReportIds, 1)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "取消发布成功"
+}
+
+// @Title 删除报告接口
+// @Description 删除报告
+// @Param	request	body models.DeleteReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /delete [post]
+func (this *ReportController) Delete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.DeleteReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ReportIds <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,报告id不可为空"
+		return
+	}
+
+	if err := services.DeleteReportAndChapter(req.ReportIds); err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除报告失败, Err: " + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除成功"
+}
+
+// @Title 新增报告接口
+// @Description 新增报告
+// @Param	request	body models.AddReq true "type json string"
+// @Success 200 {object} models.AddResp
+// @router /add [post]
+func (this *ReportController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req models.AddReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	var contentSub string
+	if req.Content != "" {
+		content, e := services.FilterReportContentBr(req.Content)
+		if e != nil {
+			br.Msg = "内容去除前后空格失败"
+			br.ErrMsg = "内容去除前后空格失败, Err: " + e.Error()
+			return
+		}
+		req.Content = content
+
+		contentSub, err = services.GetReportContentSub(req.Content)
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("ContentSub 失败,Err:"+err.Error(), 3)
+			//utils.SendEmail(utils.APPNAME+"失败提醒", "解析 ContentSub 失败,Err:"+err.Error(), utils.EmailSendToUsers)
+		}
+	}
+	maxStage, err := models.GetReportStage(req.ClassifyIdFirst, req.ClassifyIdSecond)
+	if err != nil {
+		br.Msg = "期数获取失败!"
+		br.ErrMsg = "期数获取失败,Err:" + err.Error()
+		return
+	}
+
+	item := new(models.Report)
+	item.AddType = req.AddType
+	item.ClassifyIdFirst = req.ClassifyIdFirst
+	item.ClassifyNameFirst = req.ClassifyNameFirst
+	item.ClassifyIdSecond = req.ClassifyIdSecond
+	item.ClassifyNameSecond = req.ClassifyNameSecond
+	item.Title = req.Title
+	item.Abstract = req.Abstract
+	item.Author = req.Author
+	item.Frequency = req.Frequency
+	item.State = req.State
+	item.Content = html.EscapeString(req.Content)
+	item.Stage = maxStage + 1
+	item.ContentSub = html.EscapeString(contentSub)
+	item.CreateTime = req.CreateTime
+	item.ModifyTime = time.Now()
+	item.ReportVersion = req.ReportVersion
+	item.AdminId = sysUser.AdminId
+	item.AdminRealName = sysUser.RealName
+	newReportId, err := models.AddReport(item)
+	if err != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+	//处理权限
+	{
+		permissionItems, err := models.GetPermission(req.ClassifyNameSecond)
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("获取权限失败,Err:"+err.Error(), 3)
+			//utils.SendEmail(utils.APPNAME+"失败提醒", "获取权限失败,Err:"+err.Error(), utils.EmailSendToUsers)
+		}
+		for _, v := range permissionItems {
+			err = models.AddChartPermissionChapterMapping(v.ChartPermissionId, newReportId)
+			if err != nil {
+				go alarm_msg.SendAlarmMsg("新增权限失败,Err:"+err.Error(), 3)
+				//utils.SendEmail(utils.APPNAME+"失败提醒", "新增权限失败,Err:"+err.Error()+strconv.FormatInt(newReportId, 10), utils.EmailSendToUsers)
+			}
+		}
+	}
+	reportCode := utils.MD5(strconv.Itoa(int(newReportId)))
+	//修改唯一编码
+	{
+		go models.ModifyReportCode(newReportId, reportCode)
+	}
+	resp := new(models.AddResp)
+	resp.ReportId = newReportId
+	resp.ReportCode = reportCode
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// @Title 编辑报告接口
+// @Description 编辑报告
+// @Param	request	body models.EditReq true "type json string"
+// @Success 200 {object} models.EditResp
+// @router /edit [post]
+func (this *ReportController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.EditReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.Content == "" {
+		br.Msg = "报告内容不能为空"
+		return
+	}
+	var contentSub string
+	if req.Content != "" {
+		content, e := services.FilterReportContentBr(req.Content)
+		if e != nil {
+			br.Msg = "内容去除前后空格失败"
+			br.ErrMsg = "内容去除前后空格失败, Err: " + e.Error()
+			return
+		}
+		req.Content = content
+
+		contentSub, err = services.GetReportContentSub(req.Content)
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("解析 ContentSub 失败,Err:"+err.Error(), 3)
+			//utils.SendEmail(utils.APPNAME+"失败提醒", "解析 ContentSub 失败,Err:"+err.Error(), utils.EmailSendToUsers)
+		}
+	}
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	//更新标记key
+	markStatus, err := services.UpdateReportEditMark(int(req.ReportId), sysUser.AdminId, 1, sysUser.RealName)
+	if err != nil {
+		br.Msg = err.Error()
+		return
+	}
+	if markStatus.Status == 1 {
+		br.Msg = markStatus.Msg
+		//br.Ret = 202 //202 服务器已接受请求,但尚未处理。
+		return
+	}
+
+	var stage int
+	report, _ := models.GetReportById(int(req.ReportId))
+	if report != nil {
+		if report.ClassifyNameFirst != req.ClassifyNameFirst || report.ClassifyNameSecond != req.ClassifyNameSecond {
+			maxStage, _ := models.GetReportStageEdit(req.ClassifyIdFirst, req.ClassifyIdSecond, int(req.ReportId))
+			maxStage = maxStage + 1
+			stage = maxStage
+		} else {
+			stage = report.Stage
+		}
+	}
+
+	item := new(models.Report)
+	item.ClassifyIdFirst = req.ClassifyIdFirst
+	item.ClassifyNameFirst = req.ClassifyNameFirst
+	item.ClassifyIdSecond = req.ClassifyIdSecond
+	item.ClassifyNameSecond = req.ClassifyNameSecond
+	item.Title = req.Title
+	item.Abstract = req.Abstract
+	item.Author = req.Author
+	item.Frequency = req.Frequency
+	item.State = req.State
+	item.Stage = stage
+	item.Content = html.EscapeString(req.Content)
+	item.ContentSub = html.EscapeString(contentSub)
+	item.CreateTime = req.CreateTime
+	err = models.EditReport(item, req.ReportId)
+	if err != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+	//处理权限
+	{
+		err = models.RemoveChartPermissionChapterMapping(req.ReportId)
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("修改删除报告权限失败,Err:"+err.Error(), 3)
+			//utils.SendEmail(utils.APPNAME+"失败提醒", "修改删除报告权限失败,Err:"+err.Error(), utils.EmailSendToUsers)
+		}
+		permissionItems, err := models.GetPermission(req.ClassifyNameSecond)
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("获取权限失败,Err:"+err.Error(), 3)
+			//utils.SendEmail(utils.APPNAME+"失败提醒", "获取权限失败,Err:"+err.Error(), utils.EmailSendToUsers)
+		}
+		for _, v := range permissionItems {
+			err = models.AddChartPermissionChapterMapping(v.ChartPermissionId, req.ReportId)
+			if err != nil {
+				go alarm_msg.SendAlarmMsg("新增权限失败,Err:"+err.Error(), 3)
+				//utils.SendEmail(utils.APPNAME+"失败提醒", "新增权限失败,Err:"+err.Error()+strconv.FormatInt(req.ReportId, 10), utils.EmailSendToUsers)
+			}
+		}
+	}
+	reportCode := utils.MD5(strconv.Itoa(int(req.ReportId)))
+	resp := new(models.EditResp)
+	resp.ReportId = req.ReportId
+	resp.ReportCode = reportCode
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// @Title 获取报告详情接口
+// @Description 获取报告详情
+// @Param	request	body models.ReportDetailReq true "type json string"
+// @Success 200 {object} models.Report
+// @router /detail [get]
+func (this *ReportController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	/*var req models.ReportDetailReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ReportId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}*/
+	reportId, err := this.GetInt("ReportId")
+	if err != nil {
+		br.Msg = "获取参数失败!"
+		br.ErrMsg = "获取参数失败,Err:" + err.Error()
+		return
+	}
+	if reportId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	item, err := models.GetReportById(reportId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	chapterList := make([]*models.ReportChapter, 0)
+	if item.HasChapter == 1 && (item.ChapterType == utils.REPORT_TYPE_DAY || item.ChapterType == utils.REPORT_TYPE_WEEK) {
+		// 获取章节内容
+		tmpChapterList, err := models.GetPublishedChapterListByReportId(item.Id)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取晨/周报章节列表失败, Err: " + err.Error()
+			return
+		}
+
+		if len(tmpChapterList) > 0 {
+			// 获取更新规则
+			researchType := tmpChapterList[0].ReportType
+			chapterTypeList, tmpErr := models.GetAllReportChapterTypeListByResearchType(researchType)
+			if tmpErr != nil {
+				br.Msg = "获取更新规则失败"
+				br.ErrMsg = "获取更新规则失败, Err: " + tmpErr.Error()
+				return
+			}
+			// 调整章节更新
+			nowTime := time.Now().Local()
+			for _, item := range tmpChapterList {
+				stop := false
+				for _, rule := range chapterTypeList {
+					if rule.ReportChapterTypeId == item.TypeId {
+						//fmt.Println("rule.Enabled :", rule.Enabled, ";name=", rule.ReportChapterTypeName, "item.IsEdit:", item.IsEdit, "rule.IsSet:", rule.IsSet)
+						// 如果被永久暂停更新了
+						if rule.Enabled == 0 && item.IsEdit == 0 { //该章节已被永久禁用,同时未被操作过
+							stop = true
+						} else if rule.PauseStartTime != "" && rule.PauseEndTime != "" {
+							startTime, timeErr := time.ParseInLocation(utils.FormatDate, rule.PauseStartTime, time.Local)
+							if timeErr != nil {
+								br.Msg = "获取更新规则失败"
+								br.ErrMsg = "更新规则时间转换失败4001, Err: " + timeErr.Error()
+								return
+							}
+							endTime, timeErr := time.ParseInLocation(utils.FormatDate, rule.PauseEndTime, time.Local)
+							if timeErr != nil {
+								br.Msg = "获取更新规则失败"
+								br.ErrMsg = "更新规则时间转换失败4002, Err: " + timeErr.Error()
+								return
+							}
+							// 暂停更新
+							if nowTime.After(startTime) && nowTime.Before(endTime.AddDate(0, 0, 1)) {
+								stop = true
+							}
+							break
+						}
+					}
+				}
+				if !stop {
+					item.Content = html.UnescapeString(item.Content)
+					item.ContentSub = html.UnescapeString(item.ContentSub)
+					chapterList = append(chapterList, item)
+				}
+			}
+		}
+
+		item.Abstract = item.Title
+	}
+	item.Content = html.UnescapeString(item.Content)
+	item.ContentSub = html.UnescapeString(item.ContentSub)
+
+	resp := &models.ReportDetailView{
+		ReportDetail: item,
+		ChapterList:  chapterList,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 图片上传
+// @Description 图片上传接口
+// @Param   file   query   file  true       "文件"
+// @Success 200 新增成功
+// @router /upload [post]
+func (this *ReportController) Upload() {
+	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
+	}
+	ext := path.Ext(h.Filename)
+	dateDir := time.Now().Format("20060102")
+	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
+	err = os.MkdirAll(uploadDir, 777)
+	if err != nil {
+		br.Msg = "存储目录创建失败"
+		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
+		return
+	}
+	randStr := utils.GetRandStringNoSpecialChar(28)
+	fileName := randStr + ext
+	fpath := uploadDir + "/" + fileName
+	defer f.Close() //关闭上传文件
+	err = this.SaveToFile("file", fpath)
+	if err != nil {
+		br.Msg = "文件上传失败"
+		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+		return
+	}
+	//上传到阿里云
+	resourceUrl, err := services.UploadAliyun(fileName, fpath)
+	if err != nil {
+		br.Msg = "文件上传失败"
+		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+		return
+	}
+
+	defer func() {
+		os.Remove(fpath)
+	}()
+
+	item := new(models.Resource)
+	item.ResourceUrl = resourceUrl
+	item.ResourceType = 1
+	item.CreateTime = time.Now()
+	newId, err := models.AddResource(item)
+	if err != nil {
+		br.Msg = "资源上传失败"
+		br.ErrMsg = "资源上传失败,Err:" + err.Error()
+		return
+	}
+	resp := new(models.ResourceResp)
+	resp.Id = newId
+	resp.ResourceUrl = resourceUrl
+	br.Msg = "上传成功"
+	br.Ret = 200
+	br.Success = true
+	br.Data = resp
+	return
+}
+
+// @Title 根据分类获取最近一次报告详情接口
+// @Description 根据分类获取最近一次报告详情
+// @Param	request	body models.ClassifyIdDetailReq true "type json string"
+// @Success 200 {object} models.Report
+// @router /classifyIdDetail [get]
+func (this *ReportController) ClassifyIdDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	//classify_id_first=34&classify_id_second=36
+
+	/*var req models.ClassifyIdDetailReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}*/
+	classifyIdFirst, _ := this.GetInt("classify_id_first")
+	if classifyIdFirst <= 0 {
+		classifyIdFirst, _ = this.GetInt("ClassifyIdFirst")
+	}
+	classifyIdSecond, _ := this.GetInt("classify_id_second")
+	if classifyIdSecond <= 0 {
+		classifyIdSecond, _ = this.GetInt("ClassifyIdSecond")
+	}
+	item, err := models.GetReportDetailByClassifyId(classifyIdFirst, classifyIdSecond)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败!"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	if item != nil {
+		item.Content = html.UnescapeString(item.Content)
+		item.ContentSub = html.UnescapeString(item.ContentSub)
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = item
+}
+
+// @Title 模板消息推送接口
+// @Description 模板消息推送
+// @Param	request	body models.SendTemplateMsgReq true "type json string"
+// @Success 200 Ret=200 推送成功
+// @router /sendTemplateMsg [post]
+func (this *ReportController) SendTemplateMsg() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.SendTemplateMsgReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ReportId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误"
+		return
+	}
+	report, _ := models.GetReportById(req.ReportId)
+	if report.MsgIsSend == 1 {
+		br.Msg = "消息已推送,请勿重复操作"
+		br.ErrMsg = "模板消息已推送,请勿重复操作"
+		return
+	}
+
+	videoNameDate := `(` + time.Now().Format("0102") + `)`
+	err = models.UpdateReportPublishTime(req.ReportId, videoNameDate)
+	if err != nil {
+		br.Msg = "修改报告发布时间失败"
+		br.ErrMsg = "修改发布时间失败,Err:" + err.Error()
+		return
+	}
+	err = models.UpdateReportChapterPublishTime(req.ReportId, videoNameDate)
+	if err != nil {
+		br.Msg = "修改章节发布时间失败"
+		br.ErrMsg = "修改发布时间失败,Err:" + err.Error()
+		return
+	}
+
+	services.SendMiniProgramReportWxMsg(req.ReportId)
+	err = models.ModifyReportMsgIsSend(req.ReportId)
+	if err != nil {
+		br.Msg = "发送失败"
+		br.ErrMsg = "发送失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "发送成功"
+	br.IsAddLog = true
+}
+
+// Author
+// @Title 获取报告作者接口
+// @Description 获取报告作者
+// @Param   AuthorType   query   int  true       "来源类型,1:中文,2:英文"
+// @Param   StartDate   query   string  true       "开始时间"
+// @Success 200 {object} models.ReportAuthorResp
+// @router /author [get]
+func (this *ReportController) Author() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	keyword := this.GetString("Keyword")
+
+	var condition string
+	var pars []interface{}
+	condition += ` AND author_type = 1 AND enable = 1`
+
+	if keyword != `` {
+		condition += ` AND report_author like ? `
+		pars = append(pars, "%"+keyword+"%")
+	}
+
+	_, items, err := models.GetReportAuthorList(condition, pars, 0, 10000)
+	if err != nil {
+		br.Msg = "获取失败!"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	resp := models.ReportAuthorResp{
+		List: items,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 保存草稿
+// @Description 保存草稿
+// @Param	request	body models.SaveReportContent true "type json string"
+// @Success 200 {object} models.ReportAuthorResp
+// @router /saveReportContent [post]
+func (this *ReportController) SaveReportContent() {
+	br := new(models.BaseResponse).Init()
+	br.IsSendEmail = false
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req models.SaveReportContent
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	reportId := req.ReportId
+	noChangeFlag := req.NoChange
+	fmt.Println("reportId:", reportId)
+	if reportId > 0 {
+		markStatus, err := services.UpdateReportEditMark(int(req.ReportId), sysUser.AdminId, 1, sysUser.RealName)
+		if err != nil {
+			br.Msg = err.Error()
+			return
+		}
+		if markStatus.Status == 1 {
+			br.Msg = markStatus.Msg
+			return
+		}
+	}
+	if reportId > 0 && noChangeFlag != 1 {
+		fmt.Println("line 617")
+		content := req.Content
+		if content == "" {
+			content = this.GetString("Content")
+		}
+		if content != "" {
+			contentClean, e := services.FilterReportContentBr(req.Content)
+			if e != nil {
+				br.Msg = "内容去除前后空格失败"
+				br.ErrMsg = "内容去除前后空格失败, Err: " + e.Error()
+				return
+			}
+			content = contentClean
+
+			contentSub, err := services.GetReportContentSub(content)
+			if err != nil {
+				go alarm_msg.SendAlarmMsg("解析 ContentSub 失败,Err:"+err.Error(), 3)
+				//utils.SendEmail(utils.APPNAME+"失败提醒", "解析 ContentSub 失败,Err:"+err.Error(), utils.EmailSendToUsers)
+			}
+			content = html.EscapeString(content)
+			contentSub = html.EscapeString(contentSub)
+			err = models.EditReportContent(reportId, content, contentSub)
+			if err != nil {
+				br.Msg = "保存失败"
+				br.ErrMsg = "保存失败,Err:" + err.Error()
+				return
+			}
+			go models.AddReportSaveLog(reportId, this.SysUser.AdminId, content, contentSub, this.SysUser.AdminName)
+		}
+	}
+	resp := new(models.SaveReportContentResp)
+	resp.ReportId = reportId
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功成功"
+	br.Data = resp
+}
+
+// @Title 图片上传
+// @Description 图片上传接口
+// @Param   File   query   file  true       "文件"
+// @Success 200 上传成功
+// @router /uploadImg [post]
+func (this *ReportUploadCommonController) UploadImg() {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println("文件上传失败:", err.Error())
+			go alarm_msg.SendAlarmMsg("URI:"+this.Ctx.Input.URI()+" 文件上传失败:"+err.Error(), 3)
+			//go utils.SendEmail(utils.APPNAME+"失败提醒", "URI:"+this.Ctx.Input.URI()+" 文件上传失败:"+err.Error(), utils.EmailSendToUsers)
+		}
+	}()
+	f, h, err := this.GetFile("file")
+	if err != nil {
+		return
+	}
+	ext := path.Ext(h.Filename)
+	dateDir := time.Now().Format("20060102")
+	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
+	err = os.MkdirAll(uploadDir, 777)
+	if err != nil {
+		return
+	}
+	randStr := utils.GetRandStringNoSpecialChar(28)
+	fileName := randStr + ext
+	fpath := uploadDir + "/" + fileName
+	defer f.Close() //关闭上传文件
+	err = this.SaveToFile("file", fpath)
+	if err != nil {
+		return
+	}
+	//上传到阿里云
+	resourceUrl, err := services.UploadAliyun(fileName, fpath)
+	if err != nil {
+		return
+	}
+
+	defer func() {
+		os.Remove(fpath)
+	}()
+
+	item := new(models.Resource)
+	item.ResourceUrl = resourceUrl
+	item.ResourceType = 1
+	item.CreateTime = time.Now()
+	newId, err := models.AddResource(item)
+	if err != nil {
+		return
+	}
+	resp := new(models.ResourceResp)
+	resp.Id = newId
+	resp.ResourceUrl = resourceUrl
+	this.Data["json"] = map[string]string{"link": resourceUrl}
+	this.ServeJSON()
+}
+
+// @Title 研报浏览数据导出
+// @Description 研报浏览数据导出接口
+// @Param   ReportIds   query   string  true       "报告id,多个报告用英文,隔开"
+// @Success 200 {object} company.CompanyListResp
+// @router /report_view_record/export [get]
+//func (this *ReportController) Export() {
+//	br := new(models.BaseResponse).Init()
+//	defer func() {
+//		this.Data["json"] = br
+//		this.ServeJSON()
+//	}()
+//	sysUser := this.SysUser
+//	if sysUser == nil {
+//		br.Msg = "请登录"
+//		br.ErrMsg = "请登录,SysUser Is Empty"
+//		br.Ret = 408
+//		return
+//	}
+//
+//	reportIds := this.GetString("ReportIds")
+//	if reportIds == "" {
+//		br.Msg = "请选择需要下载的报告"
+//		br.ErrMsg = "请选择需要下载的报告"
+//		return
+//	}
+//
+//	//生成excel文件
+//	dir, err := os.Executable()
+//	exPath := filepath.Dir(dir)
+//	downLoadnFilePath := exPath + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+//	xlsxFile := xlsx.NewFile()
+//	if err != nil {
+//		br.Msg = "生成文件失败"
+//		br.ErrMsg = "生成文件失败"
+//		return
+//	}
+//
+//	//只有权益研究员,以及超管可以下载
+//	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_RAI_RESEARCHR && sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+//		br.Msg = "没有下载权限"
+//		br.ErrMsg = "没有下载权限"
+//		return
+//	}
+//
+//	//普通样式
+//	style := xlsx.NewStyle()
+//	alignment := xlsx.Alignment{
+//		Horizontal: "center",
+//		Vertical:   "center",
+//		WrapText:   true,
+//	}
+//	//设置默认字体和文字大小
+//	xlsx.SetDefaultFont(12, "宋体")
+//
+//	style.Alignment = alignment
+//	style.ApplyAlignment = true
+//
+//	//标题样式
+//	titleStyle := xlsx.NewStyle()
+//	titleFont := xlsx.NewFont(20, "宋体")
+//	titleFont.Bold = true
+//	titleStyle.Font = *titleFont
+//	titleStyle.Alignment = alignment
+//	//titleStyle.ApplyAlignment = true
+//
+//	//表头
+//	headerStyle := xlsx.NewStyle()
+//	headerFont := xlsx.NewFont(12, "宋体")
+//	headerFont.Bold = true
+//	headerStyle.Font = *headerFont
+//	headerStyle.Alignment = alignment
+//	headerStyle.ApplyAlignment = true
+//
+//	defer func() {
+//		os.Remove(downLoadnFilePath)
+//	}()
+//
+//	//记录已经命名了的表单名称
+//	sheetNameMap := make(map[string]int)
+//
+//	reportSlice := strings.Split(reportIds, ",")
+//	for _, reportIdStr := range reportSlice {
+//		reportId, convErr := strconv.Atoi(reportIdStr)
+//		if convErr != nil {
+//			br.Msg = "报告传参异常"
+//			br.ErrMsg = "报告传参异常"
+//			return
+//		}
+//
+//		report, reportErr := models.GetReportById(reportId)
+//		if reportErr != nil {
+//			br.Msg = "查询报告异常"
+//			br.ErrMsg = "查询报告异常:" + reportErr.Error()
+//			return
+//		}
+//
+//		//查询浏览记录
+//		_, viewList, err := models.GetViewListByReportId(reportId)
+//		//fmt.Println(total)
+//		//fmt.Println(viewList)
+//		//fmt.Println(err)
+//		if err != nil {
+//			br.Msg = "查询报告浏览记录异常"
+//			br.ErrMsg = "查询报告浏览记录异常:" + err.Error()
+//			return
+//		}
+//
+//		//导入报表
+//		//报表名称
+//		createTime, _ := time.Parse(utils.FormatDateTime, report.CreateTime)
+//		timeStr := createTime.Format("0102")
+//		reportName := report.Title + "(" + timeStr + ")"
+//
+//		//表单名称
+//		sheetName := reportName
+//
+//		//判断当前sheet表单map中是否已经存在该表单名称,如果存在,那么需要添加后缀,并给sheet表单map中赋值次数加1处理;不存在,那么还是使用当前名称,并给sheet表单map中赋值次数为1
+//		if n, ok := sheetNameMap[sheetName]; ok == true {
+//			n++
+//			sheetName = sheetName + "_" + strconv.Itoa(n)
+//			sheetNameMap[sheetName] = n
+//		} else {
+//			sheetNameMap[sheetName] = 1
+//		}
+//
+//		sheel, err := xlsxFile.AddSheet(sheetName)
+//		if err != nil {
+//			br.Msg = "新增Sheet失败"
+//			br.ErrMsg = "新增Sheet失败,Err:" + err.Error()
+//			return
+//		}
+//		//设置列宽
+//		sheel.SetColWidth(0, 0, 28)
+//		sheel.SetColWidth(1, 1, 18)
+//		sheel.SetColWidth(2, 2, 18)
+//		sheel.SetColWidth(3, 3, 18)
+//		sheel.SetColWidth(4, 4, 40)
+//
+//		//标题行
+//		titleRow := sheel.AddRow()
+//		titleRow.SetHeight(40)
+//
+//		//标题列
+//		titleCell := titleRow.AddCell()
+//		titleCell.HMerge = 4 //向右合并列数,不包括自身列
+//
+//		//报表标题名称
+//		titleCell.SetValue(reportName)
+//		titleCell.SetStyle(titleStyle)
+//
+//		//表头
+//		headerRow := sheel.AddRow()
+//		headerRow.SetHeight(18)
+//
+//		timeHeaderCell := headerRow.AddCell()
+//		timeHeaderCell.SetValue("浏览时间")
+//		timeHeaderCell.SetStyle(headerStyle)
+//
+//		nameHeaderCell := headerRow.AddCell()
+//		nameHeaderCell.SetValue("用户名称")
+//		nameHeaderCell.SetStyle(headerStyle)
+//
+//		mobileHeaderCell := headerRow.AddCell()
+//		mobileHeaderCell.SetValue("手机号")
+//		mobileHeaderCell.SetStyle(headerStyle)
+//
+//		emailHeaderCell := headerRow.AddCell()
+//		emailHeaderCell.SetValue("邮箱")
+//		emailHeaderCell.SetStyle(headerStyle)
+//
+//		companyHeaderCell := headerRow.AddCell()
+//		companyHeaderCell.SetValue("所属企业客户名称")
+//		companyHeaderCell.SetStyle(headerStyle)
+//
+//		for _, v := range viewList {
+//			dataRow := sheel.AddRow()
+//			dataRow.SetHeight(18)
+//
+//			timeCell := dataRow.AddCell()
+//			timeCell.SetString(v.CreateTime.Format(utils.FormatDateTime))
+//			timeCell.SetStyle(style)
+//
+//			nameCell := dataRow.AddCell()
+//			nameCell.SetString(v.RealName)
+//			nameCell.SetStyle(style)
+//
+//			mobileCell := dataRow.AddCell()
+//			mobileCell.SetString(v.Mobile)
+//			mobileCell.SetStyle(style)
+//
+//			emailCell := dataRow.AddCell()
+//			emailCell.SetString(v.Email)
+//			emailCell.SetStyle(style)
+//
+//			companyCell := dataRow.AddCell()
+//			companyCell.SetString(v.CompanyName)
+//			companyCell.SetStyle(style)
+//		}
+//	}
+//
+//	err = xlsxFile.Save(downLoadnFilePath)
+//	if err != nil {
+//		br.Msg = "保存文件失败"
+//		br.ErrMsg = "保存文件失败"
+//		return
+//	}
+//	randStr := time.Now().Format(utils.FormatDateTimeUnSpace)
+//	downloadFileName := "研报浏览记录" + randStr + ".xlsx"
+//	this.Ctx.Output.Download(downLoadnFilePath, downloadFileName)
+//	br.Ret = 200
+//	br.Success = true
+//	br.Msg = "导出成功"
+//}
+
+// @Title 客群消息推送接口
+// @Description 客群消息推送接口
+// @Param	request	body models.SendTemplateMsgReq true "type json string"
+// @Success 200 Ret=200 推送成功
+// @router /ths/sendTemplateMsg [post]
+func (this *ReportController) ThsSendTemplateMsg() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.ThsSendTemplateMsgReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	for _, v := range req.ReportId {
+		if v <= 0 {
+			br.Msg = "参数错误"
+			br.ErrMsg = "参数错误"
+			return
+		}
+
+		//加锁,避免重复点击造成的多次推送
+		{
+			redisKey := fmt.Sprint("report:send:ths:", v)
+			ok := utils.Rc.SetNX(redisKey, 1, time.Second*300)
+			if !ok {
+				br.Msg = "报告已推送客群,请勿重复推送"
+				return
+			}
+			defer func() {
+				utils.Rc.Delete(redisKey)
+			}()
+		}
+
+		report, err := models.GetReportById(v)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "报告已经被删除,请刷新页面"
+				br.ErrMsg = "报告已经被删除,请刷新页面,req:" + string(this.Ctx.Input.RequestBody)
+				return
+			}
+			br.Msg = "获取报告信息失败"
+			br.ErrMsg = "获取报告信息失败,req:" + string(this.Ctx.Input.RequestBody)
+			return
+		}
+		if report.ThsMsgIsSend == 1 {
+			br.Msg = "消息已推送,请勿重复操作"
+			br.ErrMsg = "模板消息已推送,请勿重复操作1"
+			return
+		}
+		// 周报无客群
+		if report.HasChapter == 1 && report.ChapterType == utils.REPORT_TYPE_WEEK {
+			br.Msg = "周报无需推送客群消息"
+			return
+		}
+		//err = services.SendReportMiniToThs(report)
+		//if err != nil {
+		//	br.Msg = "消息已推送,请勿重复操作"
+		//	br.ErrMsg = "模板消息已推送,请勿重复操作2, Err: " + err.Error()
+		//	return
+		//}
+		err = models.ModifyReportThsMsgIsSend(report)
+		if err != nil {
+			br.Msg = "发送失败"
+			br.ErrMsg = "发送失败,Err:" + err.Error()
+			return
+		}
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "发送成功"
+}
+
+// GetDayWeekReportChapterTypeList
+// @Title 获取晨报周报章节类型列表
+// @Description 获取晨报周报章节类型列表
+// @Param	StopType	query	string	true	"停更类型 stop; disable;"
+// @Success 200 {object} models.ReportListResp
+// @router /getDayWeekReportChapterTypeList [get]
+func (this *ReportController) GetDayWeekReportChapterTypeList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var condition string
+	var pars []interface{}
+	// 停更类型
+	stopType := this.GetString("StopType")
+	switch stopType {
+	case "", "stop":
+		condition += ` AND enabled = ? `
+		pars = append(pars, 1)
+	case "disable":
+	default:
+		br.Msg = "停更类型异常"
+		br.IsSendEmail = false
+		return
+	}
+
+	dayList := make([]*models.ReportChapterType, 0)
+	weekList := make([]*models.ReportChapterType, 0)
+
+	// 晨报
+	{
+		tmpCondition := condition
+		tmpPars := pars
+		// 报告类型
+		tmpCondition += ` AND research_type = ? `
+		tmpPars = append(tmpPars, "day")
+
+		list, err := models.GetAllReportChapterTypeList(tmpCondition, tmpPars)
+		if err != nil {
+			br.Msg = "获取报告章节类型列表失败"
+			br.ErrMsg = "获取报告章节类型列表失败, Err: " + err.Error()
+			return
+		}
+		nowTime := time.Now()
+		for _, v := range list {
+			if v.IsSet == 1 {
+				endTime, _ := time.Parse(utils.FormatDate, v.PauseEndTime)
+				if nowTime.After(endTime.AddDate(0, 0, 1)) { //设置过期了已经
+					v.IsSet = 0
+					v.PauseStartTime = ``
+					v.PauseEndTime = ``
+				}
+			}
+			dayList = append(dayList, v)
+		}
+	}
+
+	// 周报
+	{
+		tmpCondition := condition
+		tmpPars := pars
+		// 报告类型
+		tmpCondition += ` AND research_type = ? `
+		tmpPars = append(tmpPars, "week")
+
+		list, err := models.GetAllReportChapterTypeList(tmpCondition, tmpPars)
+		if err != nil {
+			br.Msg = "获取报告章节类型列表失败"
+			br.ErrMsg = "获取报告章节类型列表失败, Err: " + err.Error()
+			return
+		}
+		nowTime := time.Now()
+		for _, v := range list {
+			if v.IsSet == 1 {
+				endTime, _ := time.Parse(utils.FormatDate, v.PauseEndTime)
+				if nowTime.After(endTime.AddDate(0, 0, 1)) { //设置过期了已经
+					v.IsSet = 0
+					v.PauseStartTime = ``
+					v.PauseEndTime = ``
+				}
+			}
+			weekList = append(weekList, v)
+		}
+	}
+
+	resp := models.UpdateReportChapterTypeResp{
+		Day:  dayList,
+		Week: weekList,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 获取晨报周报的更新暂停时间
+// @Description 获取晨报周报的更新暂停时间
+// @Success 200 {object} models.ReportListResp
+// @router /getDayWeekReportPauseTime [get]
+func (this *ReportController) GetDayWeekReportPauseTime() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	list, err := models.GetDayWeekReportPauseTimeList()
+	if err != nil {
+		br.Msg = "获取晨报周报的更新暂停时间失败"
+		br.ErrMsg = "获取晨报周报的更新暂停时间失败, Err: " + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = list
+}
+
+// SetDayWeekReportUpdateRule
+// @Title 设置晨报周报的更新规则
+// @Description 设置晨报周报的更新规则
+// @Param	request	body models.SetDayWeekReportUpdateRuleReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /setDayWeekReportUpdateRule [post]
+func (this *ReportController) SetDayWeekReportUpdateRule() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.SetDayWeekReportUpdateRuleReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	researchType := req.ResearchType
+	if researchType == "" {
+		br.Msg = "报告类型不能为空"
+		return
+	}
+
+	// 设置章节类型的暂停时间
+	if err := models.SetDayWeekReportUpdateRule(researchType, req.List); err != nil {
+		br.Msg = "设置暂停时间失败"
+		br.ErrMsg = "设置暂停时间失败, Err: " + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// SetDayWeekReportEnableRule
+// @Title 设置晨报周报的永久暂停更新规则
+// @Description 设置晨报周报的永久暂停更新规则
+// @Param	request	body models.SetDayWeekReportEnableUpdateRuleReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /setDayWeekReportEnableUpdateRule [post]
+func (this *ReportController) SetDayWeekReportEnableRule() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.SetDayWeekReportEnableUpdateRuleReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	dayReportChapterTypeIdList := strings.Split(req.DayReportChapterTypeId, ",")
+	weekReportChapterTypeIdList := strings.Split(req.WeekReportChapterTypeId, ",")
+	//if len(reportChapterTypeIdList) <= 0 {
+	//	br.Msg = "报告类型选择异常"
+	//	br.IsSendEmail = false
+	//	return
+	//}
+
+	// 设置章节类型的禁用状态
+	if err := models.SetDayWeekReportEnableUpdateRule(dayReportChapterTypeIdList, weekReportChapterTypeIdList); err != nil {
+		br.Msg = "设置永久停更失败"
+		br.ErrMsg = "设置永久停更失败, Err: " + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// @Title 新增晨报周报
+// @Description 新增晨报周报
+// @Param	request	body models.SaveDayWeekReportReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /addDayWeekReport [post]
+func (this *ReportController) AddDayWeekReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.SaveDayWeekReportReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	reportType := req.ReportType
+	if reportType == "" {
+		br.Msg = "请选择报告类型"
+		return
+	}
+	if reportType != utils.REPORT_TYPE_DAY && reportType != utils.REPORT_TYPE_WEEK {
+		br.Msg = "报告类型有误"
+		return
+	}
+	if req.Title == "" {
+		br.Msg = "请输入报告标题"
+		return
+	}
+	if req.CreateTime == "" {
+		br.Msg = "请选择发布时间"
+		return
+	}
+
+	// 报告
+	var frequency, classifyName string
+	var classifyId int
+	if reportType == utils.REPORT_TYPE_DAY {
+		frequency = "日度"
+		classify, err := models.GetClassifyByKeyword("晨报")
+		if err == nil {
+			classifyId = classify.Id
+			classifyName = "晨报"
+		}
+	} else if reportType == utils.REPORT_TYPE_WEEK {
+		frequency = "周度"
+		classify, err := models.GetClassifyByKeyword("周报")
+		if err == nil {
+			classifyId = classify.Id
+			classifyName = "周报"
+		}
+	} else {
+		br.Msg = "报告类型有误"
+		return
+	}
+
+	// 获取晨周报期数
+	yearStart := time.Date(time.Now().Local().Year(), 1, 1, 0, 0, 0, 0, time.Local)
+	maxStage, err := models.GetDayWeekReportStage(classifyId, yearStart)
+	if err != nil {
+		br.Msg = "获取报告期数失败"
+		br.ErrMsg = "获取报告期数失败,Err:" + err.Error()
+		return
+	}
+	stage := maxStage + 1
+
+	item := new(models.Report)
+	item.AddType = 1
+	item.ClassifyIdFirst = classifyId
+	item.ClassifyNameFirst = classifyName
+	item.Title = req.Title
+	item.Author = req.Author
+	item.Frequency = frequency
+	item.State = 1
+	item.Stage = stage
+	item.CreateTime = req.CreateTime
+	item.ModifyTime = time.Now()
+	item.HasChapter = 1
+	item.ChapterType = reportType
+
+	// 章节类型列表
+	typeList, err := models.GetReportChapterTypeListByResearchType(reportType)
+	if err != nil {
+		br.Msg = "获取报告章节类型列表失败"
+		br.ErrMsg = "获取报告章节类型列表失败,Err:" + err.Error()
+		return
+	}
+	var chapterList []*models.ReportChapter
+	// 晨报自动继承上一期内容
+	skip := false
+	if reportType == utils.REPORT_TYPE_DAY {
+		lastDayReport, err := models.GetLastPublishDayWeekReport(utils.REPORT_TYPE_DAY)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取上一期晨报失败"
+			br.ErrMsg = "获取上一期晨报失败,Err:" + err.Error()
+			return
+		}
+		if lastDayReport != nil {
+			// 继承上一篇章节内容
+			lastReportChapters, err := models.GetPublishedChapterListByReportId(lastDayReport.Id)
+			if err != nil {
+				br.Msg = "获取上一期晨报章节失败"
+				br.ErrMsg = "获取上一期晨报章节失败,Err:" + err.Error()
+				return
+			}
+			chapterMap := make(map[int]*models.ReportChapter)
+			for i := 0; i < len(lastReportChapters); i++ {
+				chapterMap[lastReportChapters[i].TypeId] = lastReportChapters[i]
+			}
+			for _, typeItem := range typeList {
+				v := chapterMap[typeItem.ReportChapterTypeId]
+				chapterItem := new(models.ReportChapter)
+				if v != nil {
+					chapterItem.AddType = 2
+					chapterItem.Title = v.Title
+					chapterItem.ReportType = reportType
+					chapterItem.ClassifyIdFirst = classifyId
+					chapterItem.ClassifyNameFirst = classifyName
+					chapterItem.TypeId = typeItem.ReportChapterTypeId
+					chapterItem.TypeName = typeItem.ReportChapterTypeName
+					chapterItem.Content = v.Content
+					chapterItem.ContentSub = v.ContentSub
+					chapterItem.Stage = stage
+					chapterItem.PublishState = 1
+					chapterItem.Sort = typeItem.Sort
+					chapterItem.CreateTime = req.CreateTime
+					chapterItem.ModifyTime = time.Now()
+				} else {
+					chapterItem.AddType = 1
+					chapterItem.ReportType = reportType
+					chapterItem.ClassifyIdFirst = classifyId
+					chapterItem.ClassifyNameFirst = classifyName
+					chapterItem.TypeId = typeItem.ReportChapterTypeId
+					chapterItem.TypeName = typeItem.ReportChapterTypeName
+					chapterItem.Stage = stage
+					chapterItem.PublishState = 1
+					chapterItem.Sort = typeItem.Sort
+					chapterItem.CreateTime = req.CreateTime
+					chapterItem.ModifyTime = time.Now()
+				}
+				chapterList = append(chapterList, chapterItem)
+			}
+			skip = true
+		}
+	}
+	if !skip {
+		// 空白章节
+		for _, typeItem := range typeList {
+			chapterItem := new(models.ReportChapter)
+			chapterItem.AddType = 1
+			chapterItem.ReportType = reportType
+			chapterItem.ClassifyIdFirst = classifyId
+			chapterItem.ClassifyNameFirst = classifyName
+			chapterItem.TypeId = typeItem.ReportChapterTypeId
+			chapterItem.TypeName = typeItem.ReportChapterTypeName
+			chapterItem.Stage = stage
+			chapterItem.PublishState = 1
+			chapterItem.Sort = typeItem.Sort
+			chapterItem.CreateTime = req.CreateTime
+			chapterItem.ModifyTime = time.Now()
+			chapterList = append(chapterList, chapterItem)
+		}
+	}
+
+	// 新增报告及章节
+	var reportId int64
+	if reportId, err = models.AddReportAndChapter(item, chapterList); err != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "新增报告及章节失败, Err: " + err.Error()
+		return
+	}
+	reportCode := utils.MD5(strconv.Itoa(int(reportId)))
+	// 修改唯一编码
+	{
+		go models.ModifyReportCode(reportId, reportCode)
+	}
+
+	resp := new(models.AddResp)
+	resp.ReportId = reportId
+	resp.ReportCode = reportCode
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// @Title 编辑晨周报
+// @Description 编辑晨周报
+// @Param	request	body models.SaveDayWeekReportReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /editDayWeekReport [post]
+func (this *ReportController) EditDayWeekReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.SaveDayWeekReportReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ReportId == 0 {
+		br.Msg = "报告ID有误"
+		return
+	}
+	if req.ReportType == "" {
+		br.Msg = "请选择报告类型"
+		return
+	}
+	if req.ReportType != utils.REPORT_TYPE_DAY && req.ReportType != utils.REPORT_TYPE_WEEK {
+		br.Msg = "报告类型有误"
+		return
+	}
+	if req.Title == "" {
+		br.Msg = "请输入报告标题"
+		return
+	}
+	if req.CreateTime == "" {
+		br.Msg = "请选择发布时间"
+		return
+	}
+
+	reportInfo, err := models.GetReportByReportId(req.ReportId)
+	if err != nil {
+		br.Msg = "获取报告信息失败"
+		br.ErrMsg = "获取报告信息失败, Err: " + err.Error()
+		return
+	}
+	if req.ReportType != reportInfo.ChapterType {
+		br.Msg = "暂不允许修改晨周报类型"
+		return
+	}
+
+	reportInfo.Title = req.Title
+	reportInfo.CreateTime = req.CreateTime
+	reportInfo.Author = req.Author
+	err = models.EditReport(reportInfo, int64(req.ReportId))
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+}
+
+// @Title 获取报告章节列表
+// @Description 获取报告章节列表
+// @Param   ReportId	query	string	true	"报告ID"
+// @Success 200 {object} company.CompanyListResp
+// @router /getReportChapterList [get]
+func (this *ReportController) GetReportChapterList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	reqReportId := this.GetString("ReportId")
+	reportId, _ := strconv.Atoi(reqReportId)
+	if reportId <= 0 {
+		br.Msg = "报告ID有误"
+		return
+	}
+
+	// 获取章节列表
+	chapterList, err := models.GetChapterListByReportId(reportId)
+	if err != nil {
+		br.Msg = "获取章节列表失败"
+		br.ErrMsg = "获取章节列表失败, Err: " + err.Error()
+		return
+	}
+	typeList, err := models.GetReportChapterTypeList()
+	if err != nil {
+		br.Msg = "获取章节类型列表失败"
+		br.ErrMsg = "获取章节类型列表失败, Err: " + err.Error()
+		return
+	}
+	typeIdImg := make(map[int]string, 0)
+	for i := 0; i < len(typeList); i++ {
+		typeIdImg[typeList[i].ReportChapterTypeId] = typeList[i].EditImgUrl
+	}
+
+	resp := make([]*models.ReportChapterResp, 0)
+	if len(chapterList) > 0 {
+		// 获取更新规则
+		researchType := chapterList[0].ReportType
+		chapterTypeList, tmpErr := models.GetAllReportChapterTypeListByResearchType(researchType)
+		if tmpErr != nil {
+			br.Msg = "获取更新规则失败"
+			br.ErrMsg = "获取更新规则失败, Err: " + tmpErr.Error()
+			return
+		}
+		// 调整章节更新
+		nowTime := time.Now().Local()
+		for _, item := range chapterList {
+			stop := false
+			for _, rule := range chapterTypeList {
+				if rule.ReportChapterTypeId == item.TypeId {
+					//fmt.Println("rule.Enabled :", rule.Enabled, ";name=", rule.ReportChapterTypeName, "item.IsEdit:", item.IsEdit, "rule.IsSet:", rule.IsSet)
+					// 如果被永久暂停更新了
+					if rule.Enabled == 0 && item.IsEdit == 0 { //该章节已被永久禁用,同时未被操作过
+						stop = true
+					} else if rule.PauseStartTime != "" && rule.PauseEndTime != "" && rule.PauseStartTime != utils.EmptyDateStr && rule.PauseEndTime != utils.EmptyDateStr {
+						startTime, timeErr := time.ParseInLocation(utils.FormatDate, rule.PauseStartTime, time.Local)
+						if timeErr != nil {
+							br.Msg = "获取更新规则失败"
+							br.ErrMsg = "更新规则时间转换失败4001, Err: " + timeErr.Error()
+							return
+						}
+						endTime, timeErr := time.ParseInLocation(utils.FormatDate, rule.PauseEndTime, time.Local)
+						if timeErr != nil {
+							br.Msg = "获取更新规则失败"
+							br.ErrMsg = "更新规则时间转换失败4002, Err: " + timeErr.Error()
+							return
+						}
+						// 暂停更新
+						if nowTime.After(startTime) && nowTime.Before(endTime.AddDate(0, 0, 1)) {
+							stop = true
+						}
+						break
+					}
+				}
+			}
+			if !stop {
+				resp = append(resp, &models.ReportChapterResp{
+					ReportChapterId:  item.ReportChapterId,
+					ReportId:         item.ReportId,
+					ReportType:       item.ReportType,
+					TypeId:           item.TypeId,
+					TypeName:         item.TypeName,
+					TypeEditImg:      typeIdImg[item.TypeId],
+					Title:            item.Title,
+					IsEdit:           item.IsEdit,
+					Trend:            item.Trend,
+					Sort:             item.Sort,
+					PublishState:     item.PublishState,
+					VideoUrl:         item.VideoUrl,
+					VideoName:        item.VideoName,
+					VideoPlaySeconds: item.VideoPlaySeconds,
+					VideoSize:        item.VideoSize,
+					VideoKind:        item.VideoKind,
+					ModifyTime:       item.ModifyTime.Format(utils.FormatDate),
+				})
+			}
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// @Title 获取上一篇已发表的晨周报章节内容
+// @Description 获取上一篇已发表的晨周报章节内容
+// @Param   TypeId		query	int		true	"品种ID"
+// @Param   ReportType	query   string  true	"报告类型 day-晨报; week-周报; "
+// @Success 200 {object} company.CompanyListResp
+// @router /getLastDayWeekReportChapter [get]
+func (this *ReportController) GetLastDayWeekReportChapter() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	typeId, _ := this.GetInt("TypeId")
+	reportType := this.GetString("ReportType")
+	item, err := models.GetLastPublishedReportChapter(typeId, reportType)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取上一篇晨周报失败, Err: " + err.Error()
+		return
+	}
+	if item != nil {
+		item.Content = html.UnescapeString(item.Content)
+		item.ContentSub = html.UnescapeString(item.ContentSub)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = item
+}
+
+// @Title 获取晨周报章节信息
+// @Description 获取晨周报章节信息
+// @Param	ReportChapterId  query  int  true  "报告章节ID"
+// @Success 200 Ret=200 保存成功
+// @router /getDayWeekChapter [get]
+func (this *ReportController) GetDayWeekChapter() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	reportChapterId, _ := this.GetInt("ReportChapterId")
+	if reportChapterId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	chapterInfo, err := models.GetReportChapterInfoById(reportChapterId)
+	if err != nil {
+		br.Msg = "获取章节信息失败"
+		br.ErrMsg = "获取章节信息失败, Err: " + err.Error()
+		return
+	}
+	if chapterInfo != nil {
+		chapterInfo.Content = html.UnescapeString(chapterInfo.Content)
+		chapterInfo.ContentSub = html.UnescapeString(chapterInfo.ContentSub)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = chapterInfo
+}
+
+// @Title 编辑晨周报章节内容
+// @Description 编辑晨周报章节内容
+// @Param	request	body models.EditReportChapterReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /editDayWeekChapter [post]
+func (this *ReportController) EditDayWeekChapter() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.EditReportChapterReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	reportChapterId := req.ReportChapterId
+	if reportChapterId <= 0 {
+		br.Msg = "报告章节ID有误"
+		return
+	}
+	//if req.Title == "" {
+	//	br.Msg = "请输入标题"
+	//	return
+	//}
+	if req.Content == "" {
+		br.Msg = "请输入内容"
+		return
+	}
+	if req.AddType == 0 {
+		br.Msg = "请选择新增方式"
+		return
+	}
+	reportChapterInfo, err := models.GetReportChapterInfoById(reportChapterId)
+	if err != nil {
+		br.Msg = "报告章节信息有误"
+		br.ErrMsg = "报告章节信息有误, Err: " + err.Error()
+		return
+	}
+	reqTickerList := req.TickerList
+	newContent := req.Content
+	// 更新章节及指标
+	contentSub := ""
+	if req.Content != "" {
+		contentClean, e := services.FilterReportContentBr(req.Content)
+		if e != nil {
+			br.Msg = "内容去除前后空格失败"
+			br.ErrMsg = "内容去除前后空格失败, Err: " + e.Error()
+			return
+		}
+		req.Content = contentClean
+
+		contentSub, err = services.GetReportContentSub(req.Content)
+		if err != nil {
+			br.Msg = "内容分段解析失败"
+			br.ErrMsg = "编辑报告章节-解析 ContentSub 失败, Err: " + err.Error()
+			return
+		}
+	}
+	if req.Content == "" {
+		req.Content = newContent
+	}
+	reportChapterInfo.Title = req.Title
+	reportChapterInfo.AddType = req.AddType
+	reportChapterInfo.Author = req.Author
+	reportChapterInfo.Content = html.EscapeString(req.Content)
+	reportChapterInfo.ContentSub = html.EscapeString(contentSub)
+	reportChapterInfo.IsEdit = 1
+	reportChapterInfo.CreateTime = req.CreateTime
+	reportChapterInfo.VideoUrl = req.VideoUrl
+	reportChapterInfo.VideoName = req.VideoName
+	reportChapterInfo.VideoPlaySeconds = req.VideoPlaySeconds
+	reportChapterInfo.VideoSize = req.VideoSize
+	reportChapterInfo.VideoKind = 1
+	updateCols := make([]string, 0)
+	updateCols = append(updateCols, "Title", "AddType", "Author", "Content", "ContentSub", "IsEdit", "CreateTime")
+	if req.VideoUrl != "" {
+		updateCols = append(updateCols, "VideoUrl", "VideoName", "VideoSize", "VideoPlaySeconds", "VideoKind")
+	}
+	// 晨报更新指标
+	tickerList := make([]*models.ReportChapterTicker, 0)
+	if reportChapterInfo.ReportType == "day" && len(reqTickerList) > 0 {
+		nowTime := time.Now()
+		for i := 0; i < len(reqTickerList); i++ {
+			tickerList = append(tickerList, &models.ReportChapterTicker{
+				ReportChapterId: reportChapterInfo.ReportChapterId,
+				Sort:            reqTickerList[i].Sort,
+				Ticker:          reqTickerList[i].Ticker,
+				CreateTime:      nowTime,
+				UpdateTime:      nowTime,
+			})
+		}
+	}
+	err = models.UpdateChapterAndTicker(reportChapterInfo, updateCols, tickerList)
+	if err != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "报告章节内容保存失败, Err: " + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+}
+
+// @Title 获取章节趋势标签列表
+// @Description 获取章节趋势标签列表
+// @Success 200 Ret=200 获取成功
+// @router /getChapterTrendTag [get]
+func (this *ReportController) GetChapterTrendTag() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	list, err := models.GetKeyWordListByFrom("trend")
+	if err != nil {
+		br.Msg = "获取章节趋势标签列表失败"
+		br.ErrMsg = "获取章节趋势标签列表失败, Err: " + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = list
+}
+
+// @Title 编辑章节趋势标签
+// @Description 编辑章节趋势标签
+// @Param	request	body models.EditReportChapterReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /editChapterTrendTag [post]
+func (this *ReportController) EditChapterTrendTag() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.EditChapterTrendTagReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ReportChapterId <= 0 {
+		br.Msg = "章节ID有误"
+		return
+	}
+	if req.Trend == "" {
+		br.Msg = "请输入标签"
+		return
+	}
+
+	chapterInfo, err := models.GetReportChapterInfoById(req.ReportChapterId)
+	if err != nil {
+		br.Msg = "获取章节信息失败"
+		br.ErrMsg = "获取章节信息失败, Err: " + err.Error()
+		return
+	}
+
+	// 更新章节标签
+	chapterInfo.Trend = req.Trend
+	updateCols := make([]string, 0)
+	updateCols = append(updateCols, "Trend")
+	if err = chapterInfo.UpdateChapter(updateCols); err != nil {
+		br.Msg = "更新标签失败"
+		br.ErrMsg = "更新标签失败, Err: " + err.Error()
+		return
+	}
+
+	// 添加关键词
+	if err = models.AddTrendTagKeyWord(req.Trend); err != nil {
+		br.Msg = "添加标签关键词失败"
+		br.ErrMsg = "添加标签关键词失败, Err: " + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+}
+
+// @Title 获取晨报数据指标列表
+// @Description 获取晨报数据指标列表
+// @Param   ReportChapterId  query  int  true  "章节ID"
+// @Success 200 Ret=200 保存成功
+// @router /getDayReportTickerList [get]
+func (this *ReportController) GetDayReportTickerList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	reportChapterId, _ := this.GetInt("ReportChapterId")
+	if reportChapterId <= 0 {
+		br.Msg = "章节ID有误"
+		return
+	}
+	chapterInfo, err := models.GetReportChapterInfoById(reportChapterId)
+	if err != nil {
+		br.Msg = "获取章节信息失败"
+		br.ErrMsg = "获取章节信息失败, Err: " + err.Error()
+		return
+	}
+
+	// 获取ticker列表
+	tickerList, err := models.GetDailyBaseColumnList("", chapterInfo.TypeId)
+	if err != nil {
+		br.Msg = "获取指标列表失败"
+		br.ErrMsg = "获取指标列表失败, Err: " + err.Error()
+		return
+	}
+	// 获取章节ticker
+	reportTicker, err := models.GetTickerListByReportChapterId(reportChapterId)
+	if err != nil {
+		br.Msg = "获取章节Ticker失败"
+		br.ErrMsg = "获取章节Ticker失败, Err: " + err.Error()
+		return
+	}
+	selectMap := make(map[string]int, 0)
+	for i := 0; i < len(reportTicker); i++ {
+		selectMap[reportTicker[i].Ticker] = 1
+	}
+	// 选中状态
+	for _, item := range tickerList {
+		if _, in := selectMap[item.BaseColumnTicker]; in {
+			item.Selected = 1
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = tickerList
+}
+
+// @Title 获取晨周报音视频列表
+// @Description 获取晨周报音视频列表
+// @Param   ReportId  query  int  true  "报告ID"
+// @Success 200 Ret=200 保存成功
+// @router /getDayWeekReportVideoList [get]
+func (this *ReportController) GetDayWeekReportVideoList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	reportId, _ := this.GetInt("ReportId")
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	list, err := models.GetReportChapterVideoList(reportId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取报告音频列表失败, Err: " + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = list
+}
+
+// @Title 是否为晨周报本期最后一个发布的报告
+// @Description 是否为晨周报本期最后一个发布的报告
+// @Param	ReportChapterId  query  int  true  "报告章节ID"
+// @Success 200 Ret=200 获取成功
+// @router /isLastDayWeekReportChapter [get]
+func (this *ReportController) IsLastDayWeekReportChapter() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	reportChapterId, _ := this.GetInt("ReportChapterId")
+	if reportChapterId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	chapterInfo, err := models.GetReportChapterInfoById(reportChapterId)
+	if err != nil {
+		br.Msg = "章节信息有误"
+		br.ErrMsg = "章节信息有误, Err: " + err.Error()
+		return
+	}
+	// 获取已发布章节数及本期报告应发布数
+	publishedNum, err := models.CountPublishedChapterNum(chapterInfo.ReportId)
+	if err != nil {
+		br.Msg = "获取已发布章节数失败"
+		br.ErrMsg = "获取已发布章节数失败, Err: " + err.Error()
+		return
+	}
+	enableTypeList, err := models.GetEnableReportChapterTypeList(chapterInfo.ReportType)
+	if err != nil {
+		br.Msg = "获取章节类型列表失败"
+		br.ErrMsg = "获取章节类型列表失败, Err: " + err.Error()
+		return
+	}
+	var isLast bool
+	enableNum := len(enableTypeList)
+	publishedNum += 1
+	if publishedNum == enableNum {
+		isLast = true
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = isLast
+}
+
+// @Title 发布晨周报章节
+// @Description 发布晨周报章节
+// @Param	request	body models.PublishReportChapterReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /publishDayWeekReportChapter [post]
+func (this *ReportController) PublishDayWeekReportChapter() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.PublishReportChapterReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ReportChapterId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	if req.Title == "" {
+		br.Msg = "请输入标题"
+		return
+	}
+	if req.Content == "" {
+		br.Msg = "请输入内容"
+		return
+	}
+	if req.AddType == 0 {
+		br.Msg = "请选择新增方式"
+		return
+	}
+
+	reqTickerList := req.TickerList
+
+	chapterInfo, err := models.GetReportChapterInfoById(req.ReportChapterId)
+	if err != nil {
+		br.Msg = "章节信息有误"
+		br.ErrMsg = "获取章节信息失败, Err: " + err.Error()
+		return
+	}
+
+	reportInfo, err := models.GetReportById(chapterInfo.ReportId)
+	if err != nil {
+		br.Msg = "查询报告有误"
+		br.ErrMsg = "查询报告信息失败, Err: " + err.Error()
+		return
+	}
+
+	// 获取规则配置
+	reportChapterTypeRule, err := models.GetReportChapterTypeById(chapterInfo.TypeId)
+	if err != nil {
+		br.Msg = "获取配置信息异常"
+		br.ErrMsg = "获取配置信息异常, Err: " + err.Error()
+		return
+	}
+	if reportChapterTypeRule.Enabled == 0 {
+		br.Msg = "该章节已永久停更"
+		br.ErrMsg = "该章节已永久停更 "
+		br.IsSendEmail = false
+		return
+	}
+
+	updateCols := make([]string, 0)
+	updateCols = append(updateCols, "Title", "AddType", "Author", "Content", "ContentSub", "IsEdit", "CreateTime", "PublishState", "PublishTime")
+
+	nowTime := time.Now()
+	var publishTime time.Time
+	if reportInfo.MsgIsSend == 1 && reportInfo.PublishTime != "" { //如果报告曾经发布过,并且已经发送过模版消息,则章节的发布时间为报告的发布时间
+		publishTime, _ = time.ParseInLocation(utils.FormatDateTime, reportInfo.PublishTime, time.Local)
+	} else {
+		publishTime = time.Now()
+	}
+	if req.VideoUrl != "" {
+		chapterInfo.VideoUrl = req.VideoUrl
+		chapterInfo.VideoName = req.VideoName
+		chapterInfo.VideoSize = req.VideoSize
+		chapterInfo.VideoPlaySeconds = req.VideoPlaySeconds
+		chapterInfo.VideoKind = 1
+
+		updateCols = append(updateCols, "VideoUrl", "VideoName", "VideoSize", "VideoPlaySeconds", "VideoKind")
+	} else {
+		if chapterInfo.VideoUrl == "" {
+			// 生成video
+			videoUrl, videoName, videoSize, videoPlaySeconds, err := services.CreateReportVideo(req.Title, req.Content, publishTime.Format(utils.FormatDateTime))
+			if err != nil {
+				br.Msg = "生成video失败"
+				br.ErrMsg = "生成video失败, Err: " + err.Error()
+				return
+			}
+			chapterInfo.VideoUrl = videoUrl
+			chapterInfo.VideoName = videoName
+			chapterInfo.VideoSize = videoSize
+			chapterInfo.VideoPlaySeconds = fmt.Sprintf("%.2f", videoPlaySeconds)
+			chapterInfo.VideoKind = 2
+			updateCols = append(updateCols, "VideoUrl", "VideoName", "VideoSize", "VideoPlaySeconds", "VideoKind")
+		}
+	}
+
+	// 更新章节信息
+	contentSub := ""
+	if req.Content != "" {
+		contentClean, e := services.FilterReportContentBr(req.Content)
+		if e != nil {
+			br.Msg = "内容去除前后空格失败"
+			br.ErrMsg = "内容去除前后空格失败, Err: " + e.Error()
+			return
+		}
+		req.Content = contentClean
+
+		contentSub, err = services.GetReportContentSub(req.Content)
+		if err != nil {
+			br.Msg = "内容分段解析失败"
+			br.ErrMsg = "编辑报告章节-解析 ContentSub 失败, Err: " + err.Error()
+			return
+		}
+	}
+	chapterInfo.Title = req.Title
+	chapterInfo.AddType = req.AddType
+	chapterInfo.Author = req.Author
+	chapterInfo.Content = html.EscapeString(req.Content)
+	chapterInfo.ContentSub = html.EscapeString(contentSub)
+	chapterInfo.IsEdit = 1
+	chapterInfo.CreateTime = req.CreateTime
+	chapterInfo.PublishState = 2
+	chapterInfo.PublishTime = publishTime
+
+	// 晨报更新指标
+	tickerList := make([]*models.ReportChapterTicker, 0)
+	if chapterInfo.ReportType == "day" && len(reqTickerList) > 0 {
+		for i := 0; i < len(reqTickerList); i++ {
+			tickerList = append(tickerList, &models.ReportChapterTicker{
+				ReportChapterId: chapterInfo.ReportChapterId,
+				Sort:            reqTickerList[i].Sort,
+				Ticker:          reqTickerList[i].Ticker,
+				CreateTime:      nowTime,
+				UpdateTime:      nowTime,
+			})
+		}
+	}
+	if err = models.UpdateChapterAndTicker(chapterInfo, updateCols, tickerList); err != nil {
+		br.Msg = "发布失败"
+		br.ErrMsg = "报告章节内容保存失败, Err: " + err.Error()
+		return
+	}
+	// 更新章节ES
+	{
+		go services.UpdateReportChapterEs(chapterInfo.ReportChapterId)
+	}
+
+	// 同时发布报告
+	if req.PublishReport == 1 {
+		if _, e := services.PublishDayWeekReport(chapterInfo.ReportId); e != nil {
+			br.Msg = "章节发布成功,报告发布失败,请手动发布报告"
+			br.ErrMsg = "发布晨/周报失败, Err: " + e.Error()
+			return
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// @Title 发布晨周报
+// @Description 发布晨周报
+// @Param	request	body models.PublishDayWeekReportReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /publishDayWeekReport [post]
+func (this *ReportController) PublishDayWeekReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.PublishDayWeekReportReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	reportInfo, err := models.GetReportByReportId(reportId)
+	if err != nil {
+		br.Msg = "报告信息有误"
+		br.ErrMsg = "报告信息有误, Err: " + err.Error()
+		return
+	}
+	if reportInfo.State == 2 {
+		br.Msg = "该报告已发布"
+		return
+	}
+	tips, err := services.PublishDayWeekReport(reportId)
+	if err != nil {
+		br.Msg = "发布失败"
+		br.ErrMsg = "发布晨/周报失败, Err: " + err.Error()
+		return
+	}
+	// 部分章节未发布的提示信息
+	if tips != "" {
+		br.Data = tips
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// GetSunCode 获取太阳码
+// @Title 公共模块
+// @Description 获取分享海报
+// @Param	request	body models.SunCodeReq true "type json string"
+// @Success 200 {object} string "获取成功"
+// @failure 400 {string} string "获取失败"
+// @router /getSunCode [post]
+//func (this *ReportController) GetSunCode() {
+//	br := new(models.BaseResponse).Init()
+//	defer func() {
+//		this.Data["json"] = br
+//		this.ServeJSON()
+//	}()
+//	sysUser := this.SysUser
+//	if sysUser == nil {
+//		br.Msg = "请登录"
+//		br.ErrMsg = "请登录,SysUser Is Empty"
+//		br.Ret = 408
+//		return
+//	}
+//	var req models.SunCodeReq
+//	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+//	if err != nil {
+//		br.Msg = "参数解析异常!"
+//		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+//		return
+//	}
+//
+//	var sunCodeUrl string
+//	//先查,查不到再去生成上传
+//	item, err := models.GetYbPcSunCode(req.CodeScene, req.CodePage)
+//	if err != nil && err.Error() != utils.ErrNoRow() {
+//		br.Msg = "查询太阳码失败!"
+//		br.ErrMsg = "查询太阳码失败,Err:" + err.Error()
+//		return
+//	}
+//	if item != nil {
+//		sunCodeUrl = item.SuncodeURL
+//	}
+//
+//	if sunCodeUrl == "" {
+//		sunCodeUrl, err = services.PcCreateAndUploadSunCode(req.CodeScene, req.CodePage)
+//		if err != nil {
+//			br.Msg = "生成太阳码失败!"
+//			br.ErrMsg = "生成太阳码失败,Err:" + err.Error()
+//			return
+//		}
+//	}
+//
+//	br.Data = sunCodeUrl
+//	br.Ret = 200
+//	br.Success = true
+//	br.Msg = "操作成功"
+//}
+
+// GetStopDayWeekReportChapterTypeList
+// @Title 获取暂停更新的晨报周报章节类型列表
+// @Description 获取暂停更新的晨报周报章节类型列表
+// @Success 200 {object} models.ReportListResp
+// @router /getStopDayWeekReportChapterTypeList [get]
+func (this *ReportController) GetStopDayWeekReportChapterTypeList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	stopDay := make([]*models.ReportChapterType, 0)
+	stopWeek := make([]*models.ReportChapterType, 0)
+	disableDay := make([]*models.ReportChapterType, 0)
+	disableWeek := make([]*models.ReportChapterType, 0)
+
+	stopList, err := models.GetStopUpdateReportChapterTypeListByResearchType()
+	if err != nil {
+		br.Msg = "获取暂停更新报告章节类型列表失败"
+		br.ErrMsg = "获取暂停更新报告章节类型列表失败, Err: " + err.Error()
+		return
+	}
+
+	for _, v := range stopList {
+		if v.ResearchType == "day" {
+			stopDay = append(stopDay, v)
+		} else {
+			stopWeek = append(stopWeek, v)
+		}
+	}
+
+	disableList, err := models.GetDisableUpdateReportChapterTypeListByResearchType()
+	if err != nil {
+		br.Msg = "获取停止更新报告章节类型列表失败"
+		br.ErrMsg = "获取停止更新报告章节类型列表失败, Err: " + err.Error()
+		return
+	}
+	for _, v := range disableList {
+		if v.ResearchType == "day" {
+			disableDay = append(disableDay, v)
+		} else {
+			disableWeek = append(disableWeek, v)
+		}
+	}
+
+	resp := models.StopUpdateReportChapterTypeResp{
+		StopDay:     stopDay,
+		StopWeek:    stopWeek,
+		DisableDay:  disableDay,
+		DisableWeek: disableWeek,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// MarkEditStatus
+// @Title 标记报告编辑状态
+// @Description 标记报告编辑状态接口
+// @Param	request	body request.MarkEditEnReport true "type json string"
+// @Success 200 标记成功 ;202 标记成功
+// @router /mark [post]
+func (this *ReportController) MarkEditStatus() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req models.MarkEditReport
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ReportId <= 0 {
+		br.Msg = "缺少报告Id"
+		return
+	}
+	if req.Status <= 0 {
+		br.Msg = "标记状态异常"
+		return
+	}
+	//更新标记key
+	data, err := services.UpdateReportEditMark(req.ReportId, sysUser.AdminId, req.Status, sysUser.RealName)
+	if err != nil {
+		br.Msg = err.Error()
+		return
+	}
+
+	msg := "标记成功"
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+	br.Data = data
+}
+
+// @Title 模板消息/客群消息推送接口
+// @Description 模板消息/客群消息推送接口
+// @Param	request	body models.SendTemplateMsgReq true "type json string"
+// @Success 200 Ret=200 推送成功
+// @router /sendMsg [post]
+func (this *ReportController) SendMsg() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.SendTemplateMsgReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ReportId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误"
+		return
+	}
+
+	// 加锁,避免重复点击造成的多次推送
+	{
+		redisKey := fmt.Sprint("report:send:ths:", req.ReportId)
+		ok := utils.Rc.SetNX(redisKey, 1, time.Second*300)
+		if !ok {
+			br.Msg = "报告已推送, 请勿重复推送"
+			return
+		}
+		defer func() {
+			_ = utils.Rc.Delete(redisKey)
+		}()
+	}
+
+	report, e := models.GetReportById(req.ReportId)
+	if e != nil {
+		br.Msg = "报告不存在或已被删除, 请刷新页面"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+	if report.MsgIsSend == 1 && report.ThsMsgIsSend == 1 {
+		br.Msg = "消息已推送,请勿重复操作"
+		br.ErrMsg = "消息已推送,请勿重复操作"
+		return
+	}
+
+	// 更新报告发布时间
+	videoNameDate := `(` + time.Now().Format("0102") + `)`
+	if err = models.UpdateReportPublishTime(req.ReportId, videoNameDate); err != nil {
+		br.Msg = "修改报告发布时间失败"
+		br.ErrMsg = "修改发布时间失败,Err:" + err.Error()
+		return
+	}
+	if err = models.UpdateReportChapterPublishTime(req.ReportId, videoNameDate); err != nil {
+		br.Msg = "修改章节发布时间失败"
+		br.ErrMsg = "修改发布时间失败,Err:" + err.Error()
+		return
+	}
+
+	// 可能存在历史数据两个只推了一个所以此处加个判断
+	// 推送模板消息
+	if report.MsgIsSend == 0 {
+		go func() {
+			fmt.Println("推送模板消息:", req.ReportId)
+			_ = services.SendMiniProgramReportWxMsg(req.ReportId)
+		}()
+	}
+	// 推送客群消息
+	if report.ThsMsgIsSend == 0 {
+		//go func() {
+		// 周报无客群消息
+		// 2023-3-19 15:19:24 现在开始周报又要推送了
+		//if report.HasChapter == 1 && report.ChapterType == utils.REPORT_TYPE_WEEK {
+		//	return
+		//}
+		//_ = services.SendReportMiniToThs(report)
+		//}()
+	}
+
+	// 更新推送消息状态
+	if err = models.ModifyReportMsgIsSendV2(req.ReportId); err != nil {
+		br.Msg = "推送失败"
+		br.ErrMsg = "更新报告消息状态失败, Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "推送成功"
+	br.IsAddLog = true
+}
+
+// GetStopDayWeekReportChapterTypeList
+// @Title 获取暂停更新的晨报周报章节类型列表
+// @Description 获取暂停更新的晨报周报章节类型列表
+// @Param   ReportId	query	string	true	"报告ID"
+// @Success 200 {object} models.ReportListResp
+// @router /CheckDayWeekReportChapterVideo [get]
+func (this *ReportController) CheckDayWeekReportChapterVideo() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	reqReportId := this.GetString("ReportId")
+	reportId, _ := strconv.Atoi(reqReportId)
+	if reportId <= 0 {
+		br.Msg = "报告ID有误"
+		return
+	}
+
+	// 获取章节列表
+	chapterList, err := models.GetChapterListByReportId(reportId)
+	if err != nil {
+		br.Msg = "获取章节列表失败"
+		br.ErrMsg = "获取章节列表失败, Err: " + err.Error()
+		return
+	}
+	var typeNameArr []string
+	for _, v := range chapterList {
+		if v.VideoUrl == "" || (v.VideoUrl != "" && v.VideoKind == 2) {
+			typeNameArr = append(typeNameArr, v.TypeName)
+		}
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = typeNameArr
+}

+ 1 - 0
go.mod

@@ -9,6 +9,7 @@ require (
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/go-sql-driver/mysql v1.7.0
 	github.com/go-xorm/xorm v0.7.9
+	github.com/gorilla/websocket v1.4.2
 	github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53
 	github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19
 	github.com/olivere/elastic/v7 v7.0.30

+ 1 - 0
go.sum

@@ -307,6 +307,7 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51
 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=

+ 1 - 0
models/classify.go

@@ -39,6 +39,7 @@ type Classify struct {
 	YbRightBanner     string    `description:"Pc端详情页,右侧,报告合集背景图"`
 	RelateTel         int       `description:"是否在电话会中可选: 0-否; 1-是"`
 	RelateVideo       int       `description:"是否在路演视频中可选: 0-否; 1-是"`
+	IsMassSend        int       `description:"1:群发,0:非群发"`
 }
 
 type ClassifyAddReq struct {

+ 48 - 0
models/permission.go

@@ -0,0 +1,48 @@
+package models
+
+import "github.com/beego/beego/v2/client/orm"
+
+// ChartPermissionSearchKeyWordMapping 权限相关
+type ChartPermissionSearchKeyWordMapping struct {
+	Id                 int    `description:"id" json:"-"`
+	ChartPermissionId  int    `description:"权限id"`
+	KeyWord            string `description:"二级分类名称"`
+	From               string `description:"类型标识" json:"-"`
+	TacticType         string `description:"策略表type字段值" json:"-"`
+	TeleconferenceSort int    `description:"电话会类型排序" json:"-"`
+}
+
+func GetPermission(classifyNameSecond string) (items []*ChartPermissionSearchKeyWordMapping, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_permission_search_key_word_mapping AS a WHERE a.from='rddp' AND a.key_word=? `
+	_, err = o.Raw(sql, classifyNameSecond).QueryRows(&items)
+	return
+}
+
+func AddChartPermissionChapterMapping(chartPermissionId int, reportId int64) (err error) {
+	sql := `INSERT INTO chart_permission_chapter_mapping (chart_permission_id, report_chapter_type_id,research_type)
+           VALUES(?,?,?)`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, chartPermissionId, reportId, "rddp").Exec()
+	return
+}
+
+func RemoveChartPermissionChapterMapping(reportId int64) (err error) {
+	sql := ` DELETE FROM chart_permission_chapter_mapping WHERE research_type=? AND report_chapter_type_id=? `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, "rddp", reportId).Exec()
+	return
+}
+
+type ChartPermissionMappingIdName struct {
+	PermissionId   int
+	PermissionName string
+}
+
+func GetChartPermissionNameFromMappingByKeyword(keyword string, source string) (list []*ChartPermissionMappingIdName, err error) {
+	o := orm.NewOrm()
+	sql := " SELECT b.chart_permission_id AS permission_id,b.permission_name FROM chart_permission_search_key_word_mapping AS a INNER JOIN chart_permission AS b ON a.chart_permission_id = b.chart_permission_id WHERE a.`from` = ? AND a.key_word = ? "
+	_, err = o.Raw(sql, source, keyword).QueryRows(&list)
+
+	return
+}

+ 1008 - 0
models/report.go

@@ -0,0 +1,1008 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+type Report struct {
+	Id                 int       `orm:"column(id)" description:"报告Id"`
+	AddType            int       `description:"新增方式:1:新增报告,2:继承报告"`
+	ClassifyIdFirst    int       `description:"一级分类id"`
+	ClassifyNameFirst  string    `description:"一级分类名称"`
+	ClassifyIdSecond   int       `description:"二级分类id"`
+	ClassifyNameSecond string    `description:"二级分类名称"`
+	Title              string    `description:"标题"`
+	Abstract           string    `description:"摘要"`
+	Author             string    `description:"作者"`
+	Frequency          string    `description:"频度"`
+	CreateTime         string    `description:"创建时间"`
+	ModifyTime         time.Time `description:"修改时间"`
+	State              int       `description:"1:未发布,2:已发布"`
+	PublishTime        time.Time `description:"发布时间"`
+	Stage              int       `description:"期数"`
+	MsgIsSend          int       `description:"消息是否已发送,0:否,1:是"`
+	ThsMsgIsSend       int       `description:"客户群消息是否已发送,0:否,1:是"`
+	Content            string    `description:"内容"`
+	VideoUrl           string    `description:"音频文件URL"`
+	VideoName          string    `description:"音频文件名称"`
+	VideoPlaySeconds   string    `description:"音频播放时长"`
+	VideoSize          string    `description:"音频文件大小,单位M"`
+	ContentSub         string    `description:"内容前两个章节"`
+	ReportCode         string    `description:"报告唯一编码"`
+	ReportVersion      int       `description:"1:旧版,2:新版"`
+	HasChapter         int       `description:"是否有章节 0-否 1-是"`
+	ChapterType        string    `description:"章节类型 day-晨报 week-周报"`
+	OldReportId        int       `description:"research_report表ID, 大于0则表示该报告为老后台同步过来的"`
+	MsgSendTime        time.Time `description:"模版消息发送时间"`
+	AdminId            int       `description:"创建者账号"`
+	AdminRealName      string    `description:"创建者姓名"`
+}
+
+type ReportList struct {
+	Id                 int                       `description:"报告Id"`
+	AddType            int                       `description:"新增方式:1:新增报告,2:继承报告"`
+	ClassifyIdFirst    int                       `description:"一级分类id"`
+	ClassifyNameFirst  string                    `description:"一级分类名称"`
+	ClassifyIdSecond   int                       `description:"二级分类id"`
+	ClassifyNameSecond string                    `description:"二级分类名称"`
+	Title              string                    `description:"标题"`
+	Abstract           string                    `description:"摘要"`
+	Author             string                    `description:"作者"`
+	Frequency          string                    `description:"频度"`
+	CreateTime         string                    `description:"创建时间"`
+	ModifyTime         time.Time                 `description:"修改时间"`
+	State              int                       `description:"1:未发布,2:已发布"`
+	PublishTime        string                    `description:"发布时间"`
+	Stage              int                       `description:"期数"`
+	MsgIsSend          int                       `description:"模板消息是否已发送,0:否,1:是"`
+	Content            string                    `description:"内容"`
+	VideoUrl           string                    `description:"音频文件URL"`
+	VideoName          string                    `description:"音频文件名称"`
+	VideoPlaySeconds   string                    `description:"音频播放时长"`
+	ContentSub         string                    `description:"内容前两个章节"`
+	Pv                 int                       `description:"Pv"`
+	Uv                 int                       `description:"Uv"`
+	ReportCode         string                    `description:"报告唯一编码"`
+	ReportVersion      int                       `description:"1:旧版,2:新版"`
+	ThsMsgIsSend       int                       `description:"客户群消息是否已发送,0:否,1:是"`
+	NeedThsMsg         int                       `description:"是否需要推送客群消息 0-否 1-是"`
+	HasChapter         int                       `description:"是否有章节 0-否 1-是"`
+	ChapterType        string                    `description:"章节类型 day-晨报 week-周报"`
+	ChapterVideoList   []*ReportChapterVideoList `description:"章节音频列表"`
+	OldReportId        int                       `description:"research_report表ID, 大于0则表示该报告为老后台同步过来的"`
+	MsgSendTime        string                    `description:"模版消息发送时间"`
+	CanEdit            bool                      `description:"是否可编辑"`
+	Editor             string                    `description:"编辑人"`
+	AdminId            int                       `description:"创建者账号"`
+	AdminRealName      string                    `description:"创建者姓名"`
+}
+
+type ReportListResp struct {
+	List   []*ReportList
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+func GetReportListCount(condition string, pars []interface{}, companyType string) (count int, err error) {
+	//产品权限
+	companyTypeSqlStr := ``
+	if companyType == "ficc" {
+		companyTypeSqlStr = " AND classify_id_first != 40 "
+	} else if companyType == "权益" {
+		companyTypeSqlStr = " AND classify_id_first = 40 "
+	}
+
+	oRddp := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT COUNT(1) AS count  FROM report WHERE 1=1 ` + companyTypeSqlStr
+	if condition != "" {
+		sql += condition
+	}
+	err = oRddp.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func GetReportList(condition string, pars []interface{}, companyType string, startSize, pageSize int) (items []*ReportList, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	//产品权限
+	companyTypeSqlStr := ``
+	if companyType == "ficc" {
+		companyTypeSqlStr = " AND classify_id_first != 40 "
+	} else if companyType == "权益" {
+		companyTypeSqlStr = " AND classify_id_first = 40 "
+	}
+
+	sql := `SELECT *,
+        (SELECT COUNT(1) FROM report_view_record AS rvr WHERE rvr.report_id=report.id) AS pv,
+        (SELECT COUNT(DISTINCT user_id) FROM report_view_record AS rvr WHERE rvr.report_id=report.id) AS uv
+        FROM report WHERE 1=1  ` + companyTypeSqlStr
+	if condition != "" {
+		sql += condition
+	}
+	sql += `ORDER BY state ASC, modify_time DESC LIMIT ?,?`
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// 发布报告
+func PublishReport(reportIds string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE report SET state=2,publish_time=now(),modify_time=NOW() WHERE id IN (` + reportIds + `)`
+	_, err = o.Raw(sql).Exec()
+	return
+}
+
+// 取消发布报告
+func PublishCancleReport(reportIds int, publishTimeNullFlag bool) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	var sql string
+	if publishTimeNullFlag {
+		sql = ` UPDATE report SET state=1, publish_time=null WHERE id =?`
+	} else {
+		sql = ` UPDATE report SET state=1 WHERE id =?`
+	}
+	_, err = o.Raw(sql, reportIds).Exec()
+	return
+}
+
+// 删除报告
+func DeleteReport(reportIds int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` DELETE FROM report WHERE id =? `
+	_, err = o.Raw(sql, reportIds).Exec()
+	return
+}
+
+type ReportDetail struct {
+	Id                 int    `orm:"column(id)" description:"报告Id"`
+	AddType            int    `description:"新增方式:1:新增报告,2:继承报告"`
+	ClassifyIdFirst    int    `description:"一级分类id"`
+	ClassifyNameFirst  string `description:"一级分类名称"`
+	ClassifyIdSecond   int    `description:"二级分类id"`
+	ClassifyNameSecond string `description:"二级分类名称"`
+	Title              string `description:"标题"`
+	Abstract           string `description:"摘要"`
+	Author             string `description:"作者"`
+	Frequency          string `description:"频度"`
+	CreateTime         string `description:"创建时间"`
+	ModifyTime         string `description:"修改时间"`
+	State              int    `description:"1:未发布,2:已发布"`
+	PublishTime        string `description:"发布时间"`
+	Stage              int    `description:"期数"`
+	MsgIsSend          int    `description:"消息是否已发送,0:否,1:是"`
+	Content            string `description:"内容"`
+	VideoUrl           string `description:"音频文件URL"`
+	VideoName          string `description:"音频文件名称"`
+	VideoPlaySeconds   string `description:"音频播放时长"`
+	ContentSub         string `description:"内容前两个章节"`
+	ThsMsgIsSend       int    `description:"客户群消息是否已发送,0:否,1:是"`
+	HasChapter         int    `description:"是否有章节 0-否 1-是"`
+	ChapterType        string `description:"章节类型 day-晨报 week-周报"`
+}
+
+func GetReportById(reportId int) (item *ReportDetail, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report WHERE id=?`
+	err = o.Raw(sql, reportId).QueryRow(&item)
+	return
+}
+
+func GetReportByIds(reportIds string) (list []*ReportDetail, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report WHERE id in ` + reportIds
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// GetSimpleReportByIds 根据报告ID查询报告基本信息
+func GetSimpleReportByIds(reportIds string) (list []*ReportDetail, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT id, title FROM report WHERE id IN (` + reportIds + `)`
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+func GetReportStage(classifyIdFirst, classifyIdSecond int) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ``
+	if classifyIdSecond > 0 {
+		sql = "SELECT MAX(stage) AS max_stage FROM report WHERE classify_id_second=? "
+		o.Raw(sql, classifyIdSecond).QueryRow(&count)
+	} else {
+		sql = "SELECT MAX(stage) AS max_stage FROM report WHERE classify_id_first=? "
+		o.Raw(sql, classifyIdFirst).QueryRow(&count)
+	}
+	return
+}
+
+func GetReportStageEdit(classifyIdFirst, classifyIdSecond, reportId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ``
+	if classifyIdSecond > 0 {
+		sql = "SELECT MAX(stage) AS max_stage FROM report WHERE classify_id_second=? AND id<>? "
+		o.Raw(sql, classifyIdSecond, reportId).QueryRow(&count)
+	} else {
+		sql = "SELECT MAX(stage) AS max_stage FROM report WHERE classify_id_first=? AND id<>? "
+		o.Raw(sql, classifyIdFirst, reportId).QueryRow(&count)
+	}
+	return
+}
+
+type PublishReq struct {
+	ReportIds string `description:"报告id,多个用英文逗号隔开"`
+}
+
+type PublishCancelReq struct {
+	ReportIds int `description:"报告id"`
+}
+
+type DeleteReq struct {
+	ReportIds int `description:"报告id"`
+}
+
+type AddReq struct {
+	AddType            int    `description:"新增方式:1:新增报告,2:继承报告"`
+	ClassifyIdFirst    int    `description:"一级分类id"`
+	ClassifyNameFirst  string `description:"一级分类名称"`
+	ClassifyIdSecond   int    `description:"二级分类id"`
+	ClassifyNameSecond string `description:"二级分类名称"`
+	Title              string `description:"标题"`
+	Abstract           string `description:"摘要"`
+	Author             string `description:"作者"`
+	Frequency          string `description:"频度"`
+	State              int    `description:"状态:1:未发布,2:已发布"`
+	Content            string `description:"内容"`
+	CreateTime         string `description:"创建时间"`
+	ReportVersion      int    `description:"1:旧版,2:新版"`
+}
+
+type AddResp struct {
+	ReportId   int64  `description:"报告id"`
+	ReportCode string `description:"报告code"`
+}
+
+func AddReport(item *Report) (lastId int64, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	lastId, err = o.Insert(item)
+	return
+}
+
+type EditReq struct {
+	ReportId           int64  `description:"报告id"`
+	ClassifyIdFirst    int    `description:"一级分类id"`
+	ClassifyNameFirst  string `description:"一级分类名称"`
+	ClassifyIdSecond   int    `description:"二级分类id"`
+	ClassifyNameSecond string `description:"二级分类名称"`
+	Title              string `description:"标题"`
+	Abstract           string `description:"摘要"`
+	Author             string `description:"作者"`
+	Frequency          string `description:"频度"`
+	State              int    `description:"状态:1:未发布,2:已发布"`
+	Content            string `description:"内容"`
+	CreateTime         string `description:"创建时间"`
+}
+
+type EditResp struct {
+	ReportId   int64  `description:"报告id"`
+	ReportCode string `description:"报告code"`
+}
+
+func EditReport(item *Report, reportId int64) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE report
+			SET
+			  classify_id_first =?,
+			  classify_name_first = ?,
+			  classify_id_second = ?,
+			  classify_name_second = ?,
+			  title = ?,
+			  abstract = ?,
+			  author = ?,
+			  frequency = ?,
+			  state = ?,
+			  content = ?,
+			  content_sub = ?,
+			  stage =?,
+			  create_time = ?,
+			  modify_time = ?
+			WHERE id = ? `
+	_, err = o.Raw(sql, item.ClassifyIdFirst, item.ClassifyNameFirst, item.ClassifyIdSecond, item.ClassifyNameSecond, item.Title,
+		item.Abstract, item.Author, item.Frequency, item.State, item.Content, item.ContentSub, item.Stage, item.CreateTime, time.Now(), reportId).Exec()
+	return
+}
+
+type ReportDetailReq struct {
+	ReportId int `description:"报告id"`
+}
+
+type ClassifyIdDetailReq struct {
+	ClassifyIdFirst  int `description:"报告一级分类id"`
+	ClassifyIdSecond int `description:"报告二级分类id"`
+}
+
+func GetReportDetailByClassifyId(classifyIdFirst, classifyIdSecond int) (item *Report, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report WHERE 1=1 `
+	if classifyIdSecond > 0 {
+		sql = sql + ` AND classify_id_second=?   ORDER BY stage DESC LIMIT 1`
+		err = o.Raw(sql, classifyIdSecond).QueryRow(&item)
+	} else {
+		sql = sql + ` AND classify_id_first=?   ORDER BY stage DESC LIMIT 1`
+		err = o.Raw(sql, classifyIdFirst).QueryRow(&item)
+	}
+	return
+}
+
+type SendTemplateMsgReq struct {
+	ReportId int `description:"报告id"`
+}
+
+func ModifyReportMsgIsSend(reportId int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	report, err := GetReportById(reportId)
+	if err != nil {
+		return
+	}
+	if report.MsgIsSend == 0 {
+		sql := `UPDATE report SET msg_is_send = 1, msg_send_time=NOW()  WHERE id = ? `
+		_, err = o.Raw(sql, reportId).Exec()
+	}
+	return
+}
+
+func ModifyReportVideo(reportId int, videoUrl, videoName, videoSize string, playSeconds float64) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE report SET video_url=?,video_name=?,video_play_seconds=?,video_size=? WHERE id=? `
+	_, err = o.Raw(sql, videoUrl, videoName, playSeconds, videoSize, reportId).Exec()
+	return
+}
+
+type ReportItem struct {
+	Id                 int       `orm:"column(id)" description:"报告Id"`
+	AddType            int       `description:"新增方式:1:新增报告,2:继承报告"`
+	ClassifyIdFirst    int       `description:"一级分类id"`
+	ClassifyNameFirst  string    `description:"一级分类名称"`
+	ClassifyIdSecond   int       `description:"二级分类id"`
+	ClassifyNameSecond string    `description:"二级分类名称"`
+	Title              string    `description:"标题"`
+	Abstract           string    `description:"摘要"`
+	Author             string    `description:"作者"`
+	Frequency          string    `description:"频度"`
+	CreateTime         time.Time `description:"创建时间"`
+	ModifyTime         time.Time `description:"修改时间"`
+	State              int       `description:"1:未发布,2:已发布"`
+	PublishTime        time.Time `description:"发布时间"`
+	Stage              int       `description:"期数"`
+	MsgIsSend          int       `description:"消息是否已发送,0:否,1:是"`
+	Content            string    `description:"内容"`
+	VideoUrl           string    `description:"音频文件URL"`
+	VideoName          string    `description:"音频文件名称"`
+	VideoPlaySeconds   string    `description:"音频播放时长"`
+	ContentSub         string    `description:"内容前两个章节"`
+}
+
+func GetReportItemById(reportId int) (item *ReportItem, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report WHERE id=?`
+	err = o.Raw(sql, reportId).QueryRow(&item)
+	return
+}
+
+type SaveReportContent struct {
+	Content  string `description:"内容"`
+	ReportId int    `description:"报告id"`
+	NoChange int    `description:"内容是否未改变:1:内容未改变"`
+}
+
+func EditReportContent(reportId int, content, contentSub string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` UPDATE report SET content=?,content_sub=?,modify_time=NOW() WHERE id=? `
+	_, err = o.Raw(sql, content, contentSub, reportId).Exec()
+	return
+}
+
+func AddReportSaveLog(reportId, adminId int, content, contentSub, adminName string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` INSERT INTO report_save_log(report_id, content,content_sub,admin_id,admin_name) VALUES (?,?,?,?,?) `
+	_, err = o.Raw(sql, reportId, content, contentSub, adminId, adminName).Exec()
+	return
+}
+
+type SaveReportContentResp struct {
+	ReportId int `description:"报告id"`
+}
+
+func ModifyReportCode(reportId int64, reportCode string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE report SET report_code=? WHERE id=? `
+	_, err = o.Raw(sql, reportCode, reportId).Exec()
+	return
+}
+
+func ModifyReportThsMsgIsSend(item *ReportDetail) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	if item.ThsMsgIsSend == 0 {
+		sql := `UPDATE report SET ths_msg_is_send = 1 WHERE id = ? `
+		_, err = o.Raw(sql, item.Id).Exec()
+	}
+	return
+}
+
+type ThsSendTemplateMsgReq struct {
+	ReportId []int `description:"报告id"`
+}
+
+type PublishDayWeekReportReq struct {
+	ReportId int `description:"报告ID"`
+}
+
+// SaveDayWeekReportReq 新增晨报周报请求体
+type SaveDayWeekReportReq struct {
+	ReportId   int    `description:"报告ID"`
+	Title      string `description:"标题"`
+	ReportType string `description:"一级分类ID"`
+	Author     string `description:"作者"`
+	CreateTime string `description:"创建时间"`
+}
+
+// GetDayWeekReportStage 获取晨报周报期数
+func GetDayWeekReportStage(classifyIdFirst int, yearStart time.Time) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := " SELECT MAX(stage) AS max_stage FROM report WHERE classify_id_first = ? AND create_time > ? "
+	o.Raw(sql, classifyIdFirst, yearStart).QueryRow(&count)
+
+	return
+}
+
+// AddReportAndChapter 新增报告及章节
+func AddReportAndChapter(reportItem *Report, chapterItemList []*ReportChapter) (reportId int64, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	if reportId, err = to.Insert(reportItem); err != nil {
+		return
+	}
+	if len(chapterItemList) > 0 {
+		for _, chapterItem := range chapterItemList {
+			chapterItem.ReportId = int(reportId)
+			if _, tmpErr := to.Insert(chapterItem); tmpErr != nil {
+				return
+			}
+		}
+	}
+
+	return
+}
+
+// GetReportByReportId 主键获取报告
+func GetReportByReportId(reportId int) (item *Report, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report WHERE id = ?`
+	err = o.Raw(sql, reportId).QueryRow(&item)
+	return
+}
+
+// GetReportByOldReportId 根据老报告id主键获取报告
+func GetReportByOldReportId(reportId int) (item *Report, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report WHERE old_report_id = ?`
+	err = o.Raw(sql, reportId).QueryRow(&item)
+	return
+}
+
+// DeleteDayWeekReportAndChapter 删除晨周报及章节
+func DeleteDayWeekReportAndChapter(reportId int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	sql := ` DELETE FROM report WHERE id = ? LIMIT 1 `
+	if _, err = to.Raw(sql, reportId).Exec(); err != nil {
+		return
+	}
+	sql = ` DELETE FROM report_chapter WHERE report_id = ? `
+	if _, err = to.Raw(sql, reportId).Exec(); err != nil {
+		return
+	}
+
+	return
+}
+
+// UpdateReport 更新报告
+func (reportInfo *Report) UpdateReport(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(reportInfo, cols...)
+
+	return
+}
+
+// 晨周报详情
+type ReportDetailView struct {
+	*ReportDetail
+	ChapterList []*ReportChapter
+}
+
+func GetUnPublishDayReport(startTime time.Time, endTime time.Time) (item *Report, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT
+				*
+			FROM
+				report AS a
+			WHERE
+				a.has_chapter = 1
+			AND a.chapter_type = "day"
+			AND a.state = 1
+			AND (
+				a.create_time BETWEEN ? AND ?
+			)
+			ORDER BY
+				a.create_time DESC
+			LIMIT 1 `
+	err = o.Raw(sql, startTime, endTime).QueryRow(&item)
+	return
+}
+
+type ElasticReportDetail struct {
+	ReportId           int    `description:"报告ID"`
+	ReportChapterId    int    `description:"报告章节ID"`
+	Title              string `description:"标题"`
+	Abstract           string `description:"摘要"`
+	BodyContent        string `description:"内容"`
+	PublishTime        string `description:"发布时间"`
+	PublishState       int    `description:"发布状态 1-未发布 2-已发布"`
+	Author             string `description:"作者"`
+	ClassifyIdFirst    int    `description:"一级分类ID"`
+	ClassifyNameFirst  string `description:"一级分类名称"`
+	ClassifyIdSecond   int    `description:"二级分类ID"`
+	ClassifyNameSecond string `description:"二级分类名称"`
+	Categories         string `description:"关联的品种名称(包括品种别名)"`
+	StageStr           string `description:"报告期数"`
+}
+
+// GetLastPublishedDayWeekReport 获取上一篇已发布的晨周报
+func GetLastPublishDayWeekReport(chapterType string) (item *Report, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report WHERE has_chapter = 1 AND chapter_type = ? AND state = 2 ORDER BY publish_time DESC LIMIT 1 `
+	err = o.Raw(sql, chapterType).QueryRow(&item)
+
+	return
+}
+
+// GetNewReportExist
+func GetNewReportExist(oldReportId int) (item *Report, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT id FROM report WHERE old_report_id = ? LIMIT 1 `
+	err = o.Raw(sql, oldReportId).QueryRow(&item)
+
+	return
+}
+
+// PublishReportAndChapter 发布报告及章节
+func PublishReportAndChapter(reportInfo *Report, publishIds string, unPublishIds string, isPublishReport bool, cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	// 更新报告
+	if isPublishReport {
+		if _, err = to.Update(reportInfo, cols...); err != nil {
+			return
+		}
+	}
+	// 发布章节
+	if publishIds != "" {
+		sql := ` UPDATE report_chapter SET publish_state = 2, publish_time = ? WHERE report_id = ? AND report_chapter_id IN (` + publishIds + `) `
+		_, err = to.Raw(sql, reportInfo.PublishTime, reportInfo.Id).Exec()
+	}
+	if unPublishIds != "" {
+		sql := ` UPDATE report_chapter SET publish_state = 1, publish_time = NULL, is_edit = 0 WHERE report_id = ? AND report_chapter_id IN (` + unPublishIds + `) `
+		_, err = to.Raw(sql, reportInfo.Id).Exec()
+	}
+	return
+}
+
+func GetSyncEmptyVideoReport() (list []*Report, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT id FROM report WHERE old_report_id > 0 AND state = 2 AND chapter_type = "" AND (video_size = "" OR video_play_seconds = "")
+UNION ALL
+SELECT DISTINCT report_id FROM report_chapter WHERE publish_state = 2 AND (video_size = "" OR video_play_seconds = "") `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// 发布报告
+func PublishReportById(reportId int, publishTime time.Time) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE report SET state = 2, publish_time = ?, modify_time = NOW() WHERE id = ? `
+	_, err = o.Raw(sql, publishTime, reportId).Exec()
+	return
+}
+
+// GetCommentReportByReportId 查询有留言的报告列表
+func GetCommentReportByReportId(condition string, pars []interface{}, startSize, pageSize int) (list []*Report, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT
+	id,
+	create_time,
+	title,
+	classify_name_first,
+	classify_id_first,
+	classify_name_second,
+	classify_id_second,
+	state,
+IF
+	( publish_time, publish_time, create_time ) AS publish_time 
+FROM
+	report 
+WHERE
+	1=1
+  `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY  publish_time DESC , title ASC LIMIT ?,?`
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&list)
+	return
+}
+
+// GetCommentReportByReportIdOrderBy 查询有留言的报告列表(指定排序)
+func GetCommentReportByReportIdOrderBy(condition string, pars []interface{}, startSize, pageSize int, orderBy string) (list []*Report, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT
+	id,
+	create_time,
+	title,
+	classify_name_first,
+	classify_id_first,
+	classify_name_second,
+	classify_id_second,
+	state,
+IF
+	( publish_time, publish_time, create_time ) AS publish_time 
+FROM
+	report 
+WHERE
+	1=1
+  `
+	if condition != "" {
+		sql += condition
+	}
+	if orderBy == `` {
+		sql += ` ORDER BY  publish_time DESC , title ASC `
+	} else {
+		sql += orderBy
+	}
+	sql += ` LIMIT ?,? `
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&list)
+	return
+}
+
+// GetCommentReportTotalByReportId 查询有留言的报告列表总数
+func GetCommentReportTotalByReportId(condition string, pars []interface{}) (total int64, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT count(*)
+        FROM report WHERE 1=1`
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&total)
+	return
+}
+
+// 点赞相关的报告列表
+type LikeReportItem struct {
+	ReportId              int       `description:"报告Id"`
+	ReportChapterId       int       `description:"报告章节Id"`
+	ClassifyIdFirst       int       `description:"一级分类id"`
+	ClassifyNameFirst     string    `description:"一级分类名称"`
+	ClassifyIdSecond      int       `description:"二级分类id"`
+	ClassifyNameSecond    string    `description:"二级分类名称"`
+	ReportChapterTypeId   int       `description:"章节类型"`
+	ReportChapterTypeName string    `description:"品种名称"`
+	PublishTime           time.Time `description:"发布时间" `
+	Title                 string    `description:"标题"`
+}
+
+// GetLikeReportByReportIdReportChapterId 获取有被点赞的报告列表
+func GetLikeReportByReportIdReportChapterId(reportIds string, chapterIds string, orderStr string, startSize, pageSize int) (list []*LikeReportItem, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `( SELECT
+id AS report_id,
+0 AS report_chapter_id,
+classify_id_first,
+classify_id_second,
+classify_name_first,
+classify_name_second,
+0 as report_chapter_type_id,
+"" as report_chapter_type_name,
+publish_time,
+title
+FROM
+	report
+WHERE
+	classify_name_first != "晨报" 
+	AND classify_name_first != "周报"
+	AND id in (` + reportIds + `)
+	)
+UNION
+	
+( SELECT
+report_id,
+report_chapter_id,
+classify_id_first,
+0 as classify_id_second,
+classify_name_first,
+null as classify_name_second,
+type_id as report_chapter_type_id,
+type_name as report_chapter_type_name,
+publish_time,
+title
+FROM
+	report_chapter
+WHERE
+	 report_chapter_id in (` + chapterIds + `)
+	)`
+	if orderStr != "" {
+		sql += ` ORDER BY FIELD(CONCAT(report_id, "-",report_chapter_id),` + orderStr + `)`
+	} else {
+		sql += ` ORDER BY  publish_time DESC, report_id Desc`
+	}
+	sql += ` LIMIT ?,?`
+	_, err = o.Raw(sql, startSize, pageSize).QueryRows(&list)
+	return
+}
+
+// GetLikeReportTotalByReportIdReportChapterId 获取有被点赞的报告列表总数
+func GetLikeReportTotalByReportIdReportChapterId(reportIds string, chapterIds string) (total int64, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `select count(*) from (( SELECT
+id AS report_id,
+0 AS report_chapter_id,
+classify_id_first,
+classify_id_second,
+classify_name_first,
+classify_name_second,
+0 as report_chapter_type_id,
+publish_time,
+title
+FROM
+	report
+WHERE
+	classify_name_first != "晨报" 
+	AND classify_name_first != "周报"
+	AND id in (` + reportIds + `)
+	)
+UNION
+	
+( SELECT
+report_id,
+report_chapter_id,
+classify_id_first,
+0 as classify_id_second,
+classify_name_first,
+null as classify_name_second,
+type_id as report_chapter_type_id,
+publish_time,
+title
+FROM
+	report_chapter
+WHERE
+report_chapter_id in (` + chapterIds + `)
+	)) r`
+	err = o.Raw(sql).QueryRow(&total)
+	return
+}
+
+// GetPageReportList 分页获取报告列表
+func GetPageReportList(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*ReportList, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report WHERE 1=1 `
+	sql += condition
+	sql += ` ORDER BY modify_time DESC`
+	totalSql := `SELECT COUNT(1) total FROM (` + sql + `) z `
+	err = o.Raw(totalSql, pars).QueryRow(&total)
+	if err != nil {
+		return
+	}
+	sql += ` LIMIT ?,? `
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// SunCodeReq 获取太阳码请求体
+type SunCodeReq struct {
+	CodePage  string `json:"CodePage" description:"太阳码page"`
+	CodeScene string `json:"CodeScene" description:"太阳码scene"`
+}
+
+// YbPcSuncode 活动海报表
+type YbPcSuncode struct {
+	SuncodeID  uint32    `orm:"column(suncode_id);pk" `
+	Scene      string    `gorm:"column:scene;type:varchar(255);not null;default:0" json:"scene"` // 微信scene
+	SceneMd5   string    `gorm:"column:scene_md5;type:varchar(255);not null" json:"sceneMd5"`
+	CodePage   string    `gorm:"column:code_page;type:varchar(255);not null;default:''" json:"codePage"`     // 路径
+	SuncodeURL string    `gorm:"column:suncode_url;type:varchar(255);not null;default:''" json:"suncodeUrl"` // 太阳码储存地址
+	CreateTime time.Time `gorm:"column:create_time;type:timestamp;default:CURRENT_TIMESTAMP" json:"createTime"`
+}
+
+// GetYbPcSunCode 获取太阳码
+func GetYbPcSunCode(scene, page string) (item *YbPcSuncode, err error) {
+	o := orm.NewOrmUsingDB("default")
+	sql := `SELECT * FROM yb_pc_suncode WHERE scene = ? AND code_page = ? `
+	err = o.Raw(sql, scene, page).QueryRow(&item)
+	return
+}
+
+func AddYbPcSunCode(item *YbPcSuncode) (err error) {
+	o := orm.NewOrmUsingDB("default")
+	_, err = o.Insert(item)
+	return
+}
+
+// YbSuncodePars 小程序太阳码scene参数
+type YbSuncodePars struct {
+	ID         uint32    `orm:"column(id);pk" `
+	Scene      string    `gorm:"column:scene;type:varchar(255);not null;default:''" json:"scene"`        // scene参数
+	SceneKey   string    `gorm:"column:scene_key;type:varchar(32);not null;default:''" json:"scene_key"` // MD5值
+	CreateTime time.Time `gorm:"column:create_time;type:datetime;default:CURRENT_TIMESTAMP" json:"createTime"`
+}
+
+func AddYbSuncodePars(item *YbSuncodePars) (err error) {
+	o := orm.NewOrmUsingDB("default")
+	_, err = o.Insert(item)
+	return
+}
+
+// UpdateReportSecondClassifyNameByClassifyId 更新报告分类名称字段
+func UpdateReportSecondClassifyNameByClassifyId(classifyId int, classifyName string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := " UPDATE report SET classify_name_second = ? WHERE classify_id_second = ? "
+	_, err = o.Raw(sql, classifyName, classifyId).Exec()
+	return
+}
+
+// UpdateReportFirstClassifyNameByClassifyId 更新报告分类一级名称字段
+func UpdateReportFirstClassifyNameByClassifyId(classifyId int, classifyName string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := " UPDATE report SET classify_name_first = ? WHERE classify_id_first = ? "
+	_, err = o.Raw(sql, classifyName, classifyId).Exec()
+	return
+}
+
+// UpdateReportSecondClassifyFirstNameByClassifyId 更新报告二级分类的一级分类名称和id
+func UpdateReportSecondClassifyFirstNameByClassifyId(classifyId, newClassifyId int, classifyName string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := " UPDATE report SET classify_name_first = ?, classify_id_first = ? WHERE classify_id_second = ? "
+	_, err = o.Raw(sql, classifyName, newClassifyId, classifyId).Exec()
+	return
+}
+
+// GetEmptyContentSubPPTReport 获取前两章为空的PPT报告
+func GetEmptyContentSubPPTReport() (list []*Report, err error) {
+	sql := `SELECT
+				r.id,
+				r.content,
+				r.content_sub
+			FROM
+				report AS r
+			JOIN ppt_v2 AS p ON r.id = p.report_id
+			WHERE
+				p.report_id > 0 AND r.content_sub = ""`
+	_, err = orm.NewOrmUsingDB("rddp").Raw(sql).QueryRows(&list)
+	return
+}
+
+// ModifyReportAuthor 更改报告作者
+func ModifyReportAuthor(condition string, pars []interface{}, authorName string) (count int, err error) {
+	//产品权限
+	oRddp := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE english_report set author = ? WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = oRddp.Raw(sql, authorName, pars).QueryRow(&count)
+	return
+}
+
+func UpdateReportPublishTime(reportId int, videoNameDate string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql1 := ` UPDATE report SET publish_time = NOW() WHERE id = ?  `
+	_, err = o.Raw(sql1, reportId).Exec()
+	if err != nil {
+		return
+	}
+	//修改音频标题
+	sql2 := ` UPDATE report SET video_name=CONCAT(SUBSTRING_INDEX(video_name,"(",1),"` + videoNameDate + `") WHERE id = ? and (video_name !="" and video_name is not null)`
+	_, err = o.Raw(sql2, reportId).Exec()
+	return
+}
+
+func UpdateReportChapterPublishTime(reportId int, videoNameDate string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql1 := ` UPDATE report_chapter SET publish_time = NOW() WHERE report_id = ? `
+	_, err = o.Raw(sql1, reportId).Exec()
+	if err != nil {
+		return
+	}
+	//修改音频标题
+	sql2 := ` UPDATE report_chapter SET video_name=CONCAT(SUBSTRING_INDEX(video_name,"(",1),"` + videoNameDate + `") WHERE report_id = ? and (video_name !="" and video_name is not null)`
+	_, err = o.Raw(sql2, reportId).Exec()
+	return
+}
+
+// MarkEditReport 标记编辑英文研报的请求数据
+type MarkEditReport struct {
+	ReportId int `description:"研报id"`
+	Status   int `description:"标记状态,1:编辑中,2:编辑完成"`
+}
+
+type MarkReportResp struct {
+	Status int    `description:"状态:0:无人编辑, 1:当前有人在编辑"`
+	Msg    string `description:"提示信息"`
+	Editor string `description:"编辑者姓名"`
+}
+
+type MarkReportItem struct {
+	AdminId                 int    `description:"编辑者ID"`
+	Editor                  string `description:"编辑者姓名"`
+	ReportClassifyNameFirst string
+}
+
+// GetReportByCondition 获取报告
+func GetReportByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, isPage bool, startSize, pageSize int) (items []*Report, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := `*`
+	if len(fieldArr) > 0 {
+		fields = strings.Join(fieldArr, ",")
+	}
+	sql := `SELECT ` + fields + ` FROM report WHERE 1=1 `
+	sql += condition
+	order := ` ORDER BY modify_time DESC`
+	if orderRule != `` {
+		order = orderRule
+	}
+	sql += order
+	if isPage {
+		sql += ` LIMIT ?,?`
+		_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	} else {
+		_, err = o.Raw(sql, pars).QueryRows(&items)
+	}
+	return
+}
+
+// ModifyReportMsgIsSendV2 更新报告消息状态
+func ModifyReportMsgIsSendV2(reportId int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE report SET msg_is_send = 1, ths_msg_is_send = 1, msg_send_time = NOW() WHERE id = ? LIMIT 1`
+	_, err = o.Raw(sql, reportId).Exec()
+	return
+}

+ 109 - 0
models/report_author.go

@@ -0,0 +1,109 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+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(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*ReportAuthor, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	baseSql := `  report_author WHERE is_delete=0 `
+	if condition != "" {
+		baseSql += condition
+	}
+
+	totalSql := ` SELECT count(1) total FROM ` + baseSql
+	err = o.Raw(totalSql, pars).QueryRow(&total)
+	if err != nil {
+		return
+	}
+
+	sql := ` SELECT * FROM ` + baseSql + ` ORDER BY id desc LIMIT ?,? `
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// GetReportAuthorCount 获取报告作者列表数
+func GetReportAuthorCount(condition string, pars []interface{}) (total int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+
+	totalSql := ` SELECT count(1) total FROM report_author WHERE 1=1 `
+	if condition != "" {
+		totalSql += condition
+	}
+	err = o.Raw(totalSql, pars).QueryRow(&total)
+	return
+}
+
+type ReportAuthorResp struct {
+	List   []*ReportAuthor
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+// AddReportAuthorReq 新增报告作者请求体
+type AddReportAuthorReq struct {
+	Id         int    `description:"作者id"`
+	AuthorType int    `description:"类型,1:中文;2:英文"`
+	Author     string `description:"报告作者名称"`
+}
+
+// GetReportAuthorById 根据作者id获取数据
+func GetReportAuthorById(authorId int) (item *ReportAuthor, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report_author WHERE is_delete=0 AND id = ? `
+	err = o.Raw(sql, authorId).QueryRow(&item)
+	return
+}
+
+// GetReportAuthorByAuthor 根据作者名称获取数据
+func GetReportAuthorByAuthor(title string, authorType int) (item *ReportAuthor, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report_author WHERE is_delete=0 AND report_author=? AND author_type = ? `
+	err = o.Raw(sql, title, authorType).QueryRow(&item)
+	return
+}
+
+// GetReportAuthorByAuthorAndId 根据作者名称和作者id获取数据
+func GetReportAuthorByAuthorAndId(title string, authorType, authorId int) (item *ReportAuthor, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report_author WHERE is_delete=0 AND report_author=? AND author_type = ? AND id != ? `
+	err = o.Raw(sql, title, authorType, authorId).QueryRow(&item)
+	return
+}
+
+// AddReportAuthor 新增作者
+func AddReportAuthor(item *ReportAuthor) (lastId int64, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	lastId, err = o.Insert(item)
+	return
+}
+
+// Update 更新作者基础信息
+func (item *ReportAuthor) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(item, cols...)
+	return
+}
+
+// EnableReportAuthorReq 启用/禁用报告作者请求体
+type EnableReportAuthorReq struct {
+	Id         int `description:"作者id"`
+	EnableType int `description:"是否启用,0:禁用,1:启用"`
+}
+
+// DeleteReportAuthorReq 删除报告作者请求体
+type DeleteReportAuthorReq struct {
+	Id int `description:"作者id"`
+}

+ 304 - 0
models/report_chapter.go

@@ -0,0 +1,304 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// ReportChapter 报告章节
+type ReportChapter struct {
+	ReportChapterId   int       `orm:"column(report_chapter_id);pk" description:"报告章节ID"`
+	ReportId          int       `description:"报告ID"`
+	ReportType        string    `description:"报告类型 day-晨报 week-周报"`
+	ClassifyIdFirst   int       `description:"一级分类id"`
+	ClassifyNameFirst string    `description:"一级分类名称"`
+	TypeId            int       `description:"品种ID"`
+	TypeName          string    `description:"品种名称"`
+	Title             string    `description:"标题"`
+	Abstract          string    `description:"摘要"`
+	AddType           int       `description:"新增方式:1:新增报告,2:继承报告"`
+	Author            string    `description:"作者"`
+	Content           string    `description:"内容"`
+	ContentSub        string    `description:"内容前两个章节"`
+	Stage             int       `description:"期数"`
+	Trend             string    `description:"趋势观点"`
+	Sort              int       `description:"排序: 数值越小越靠前"`
+	IsEdit            int       `description:"是否已编辑 0-待编辑 1-已编辑"`
+	PublishState      int       `description:"发布状态 1-待发布,2-已发布"`
+	PublishTime       time.Time `description:"发布时间"`
+	VideoUrl          string    `description:"音频文件URL"`
+	VideoName         string    `description:"音频文件名称"`
+	VideoPlaySeconds  string    `description:"音频播放时长"`
+	VideoSize         string    `description:"音频文件大小,单位M"`
+	VideoKind         int       `description:"音频生成方式:1,手动上传,2:自动生成"`
+	CreateTime        string    `description:"创建时间"`
+	ModifyTime        time.Time `description:"修改时间"`
+}
+
+type ReportChapterResp struct {
+	ReportChapterId  int    `description:"报告章节ID"`
+	ReportId         int    `description:"报告ID"`
+	ReportType       string `description:"报告类型 day-晨报 week-周报"`
+	TypeId           int    `description:"品种ID"`
+	TypeName         string `description:"品种名称"`
+	TypeEditImg      string `description:"后台编辑时的图片"`
+	Title            string `description:"标题"`
+	Abstract         string `description:"摘要"`
+	Author           string `description:"作者"`
+	Content          string `description:"内容"`
+	ContentSub       string `description:"内容前两个章节"`
+	Stage            int    `description:"期数"`
+	Trend            string `description:"趋势观点"`
+	Sort             int    `description:"排序: 数值越小越靠前"`
+	IsEdit           int    `description:"是否已编辑 0-待编辑 1-已编辑"`
+	PublishState     int    `description:"发布状态 1-待发布,2-已发布"`
+	VideoUrl         string `description:"音频文件URL"`
+	VideoName        string `description:"音频文件名称"`
+	VideoPlaySeconds string `description:"音频播放时长"`
+	VideoSize        string `description:"音频文件大小,单位M"`
+	VideoKind        int    `description:"音频生成方式:1,手动上传,2:自动生成"`
+	PublishTime      string `description:"发布时间"`
+	CreateTime       string `description:"创建时间"`
+	ModifyTime       string `description:"修改时间"`
+}
+
+// GetChapterListByReportId 根据ReportId获取章节列表
+func GetChapterListByReportId(reportId int) (list []*ReportChapter, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter WHERE report_id = ? ORDER BY sort ASC`
+	_, err = o.Raw(sql, reportId).QueryRows(&list)
+
+	return
+}
+
+// GetPublishedChapterListByReportId 根据ReportId获取已发布章节列表
+func GetPublishedChapterListByReportId(reportId int) (list []*ReportChapter, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter WHERE report_id = ? AND publish_state = 2 ORDER BY sort ASC`
+	_, err = o.Raw(sql, reportId).QueryRows(&list)
+
+	return
+}
+
+// EditReportChapterReq 编辑报告章节请求体
+type EditReportChapterReq struct {
+	ReportChapterId  int            `description:"报告章节ID"`
+	Title            string         `description:"标题"`
+	AddType          int            `description:"新增方式:1:新增报告,2:继承报告"`
+	Author           string         `description:"作者"`
+	Content          string         `description:"内容"`
+	TickerList       []EditTickList `description:"指标信息"`
+	CreateTime       string         `description:"发布时间"`
+	VideoUrl         string         `description:"音频文件URL"`
+	VideoName        string         `description:"音频文件名称"`
+	VideoPlaySeconds string         `description:"音频播放时长"`
+	VideoSize        string         `description:"音频文件大小,单位M"`
+}
+
+type EditTickList struct {
+	Label  string
+	Ticker string
+	Sort   int
+}
+
+// GetReportChapterInfoById 根据主键获取报告章节
+func GetReportChapterInfoById(reportChapterId int) (item *ReportChapter, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter WHERE report_chapter_id = ? `
+	err = o.Raw(sql, reportChapterId).QueryRow(&item)
+
+	return
+}
+
+// GetLastPublishedReportChapter 获取上一篇已发表的晨周报章节
+func GetLastPublishedReportChapter(typeId int, reportType string) (item *ReportChapter, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter WHERE publish_state = 2 AND type_id = ? AND report_type = ? ORDER BY report_chapter_id DESC limit 1 `
+	err = o.Raw(sql, typeId, reportType).QueryRow(&item)
+
+	return
+}
+
+// UpdateChapter 更新报表章节
+func (chapterInfo *ReportChapter) UpdateChapter(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(chapterInfo, cols...)
+
+	return
+}
+
+// EditChapterTrendTagReq 编辑章节趋势标签请求体
+type EditChapterTrendTagReq struct {
+	ReportChapterId int    `description:"章节ID"`
+	Trend           string `description:"趋势"`
+}
+
+// UpdateChapterAndTicker 更新章节及ticker
+func UpdateChapterAndTicker(chapterInfo *ReportChapter, updateCols []string, tickerList []*ReportChapterTicker) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 更新章节
+	if err = chapterInfo.UpdateChapter(updateCols); err != nil {
+		return
+	}
+	// 清空并新增章节ticker
+	if err = ClearReportChapterTicker(chapterInfo.ReportChapterId); err != nil {
+		return
+	}
+	tickerLen := len(tickerList)
+	if tickerLen > 0 {
+		for i := 0; i < tickerLen; i++ {
+			_, tmpErr := InsertChapterTicker(tickerList[i])
+			if tmpErr != nil {
+				return
+			}
+		}
+	}
+
+	return
+}
+
+// ReportChapterVideoList 报告章节音频列表
+type ReportChapterVideoList struct {
+	ReportId         int    `description:"报告ID"`
+	ReportChapterId  int    `description:"报告章节ID"`
+	VideoUrl         string `description:"音频文件URL"`
+	VideoName        string `description:"音频文件名称"`
+	VideoPlaySeconds string `description:"音频播放时长"`
+}
+
+// GetReportChapterVideoList 获取报告章节音频列表
+func GetReportChapterVideoList(reportId int) (list []*ReportChapterVideoList, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT
+				report_id,
+				report_chapter_id,
+				video_url,
+				video_name,
+				video_play_seconds
+			FROM
+				report_chapter
+			WHERE
+				report_id = ? AND publish_state = 2 AND video_url != ""
+			ORDER BY
+				report_chapter_id ASC `
+	_, err = o.Raw(sql, reportId).QueryRows(&list)
+
+	return
+}
+
+// GetReportChapterVideoListByReportIds 根据报告ID集合获取报告章节音频列表
+func GetReportChapterVideoListByReportIds(reportIds string) (list []*ReportChapterVideoList, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT
+				report_id,
+				report_chapter_id,
+				video_url,
+				video_name,
+				video_play_seconds
+			FROM
+				report_chapter
+			WHERE
+				report_id IN (` + reportIds + `) AND publish_state = 2 AND video_url != ""
+			ORDER BY
+				report_chapter_id ASC `
+	_, err = o.Raw(sql).QueryRows(&list)
+
+	return
+}
+
+// GetReportChapterVideoListByChapterIds 根据章节ID集合获取报告章节音频列表
+func GetReportChapterVideoListByChapterIds(chapterIds string) (list []*ReportChapterVideoList, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT
+				report_id,
+				report_chapter_id,
+				video_url,
+				video_name,
+				video_play_seconds
+			FROM
+				report_chapter
+			WHERE
+				report_chapter_id IN (` + chapterIds + `) AND publish_state = 2
+			ORDER BY
+				report_chapter_id ASC `
+	_, err = o.Raw(sql).QueryRows(&list)
+
+	return
+}
+
+// PublishReportChapterReq 发布报告章节请求体
+type PublishReportChapterReq struct {
+	ReportChapterId  int            `description:"报告章节ID"`
+	Title            string         `description:"标题"`
+	AddType          int            `description:"新增方式:1:新增报告,2:继承报告"`
+	Author           string         `description:"作者"`
+	Content          string         `description:"内容"`
+	TickerList       []EditTickList `description:"指标信息"`
+	CreateTime       string         `description:"发布时间"`
+	PublishReport    int            `description:"是否同时发布报告"`
+	VideoUrl         string         `description:"音频文件URL"`
+	VideoName        string         `description:"音频文件名称"`
+	VideoPlaySeconds string         `description:"音频播放时长"`
+	VideoSize        string         `description:"音频文件大小,单位M"`
+}
+
+// CountPublishedChapterNum 获取报告已发布的章节数
+func CountPublishedChapterNum(reportId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT COUNT(1) AS ct FROM report_chapter WHERE report_id = ? AND publish_state = 2 `
+	err = o.Raw(sql, reportId).QueryRow(&count)
+
+	return
+}
+
+// GetChapterListByReportId 根据ReportId获取章节列表
+func GetChapterListByChapterIds(chapterIds string) (list []*ReportChapter, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter WHERE report_chapter_id IN (` + chapterIds + `) ORDER BY sort ASC`
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// GetChapterSimpleListByChapterIds 根据章节ID获取章节列表
+func GetChapterSimpleListByChapterIds(chapterIds string) (list []*ReportChapter, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT report_id, report_chapter_id, title, type_name, create_time, IF(publish_time,publish_time,create_time) as publish_time FROM report_chapter WHERE report_chapter_id IN (` + chapterIds + `) ORDER BY publish_time desc, sort ASC`
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// GetChapterSimpleListByReportIds 根据ReportId获取章节列表
+func GetChapterSimpleListByReportIds(reportIds string) (list []*ReportChapter, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT report_id, report_chapter_id, title, type_name, create_time, IF(publish_time,publish_time,create_time) as publish_time FROM report_chapter WHERE publish_state = 2 and report_id IN (` + reportIds + `) ORDER BY publish_time desc, sort ASC`
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// UpdateReportChapterTypeNameByTypeId 更新章节类型名称
+func UpdateReportChapterTypeNameByTypeId(typeId int, typeName string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE report_chapter SET type_name = ? WHERE type_id = ?`
+	_, err = o.Raw(sql, typeName, typeId).Exec()
+	return
+}
+
+// CountReportChapterByTypeId 通过章节类型ID获取章节数
+func CountReportChapterByTypeId(typeId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT COUNT(1) AS ct FROM report_chapter WHERE type_id = ?`
+	err = o.Raw(sql, typeId).QueryRow(&count)
+	return
+}

+ 73 - 0
models/report_chapter_ticker.go

@@ -0,0 +1,73 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type ReportChapterTicker struct {
+	Id              int       `orm:"column(id);pk" description:"主键ID"`
+	ReportChapterId int       `description:"报告章节ID"`
+	Sort            int       `description:"排序"`
+	Ticker          string    `description:"ticker"`
+	CreateTime      time.Time `description:"创建时间"`
+	UpdateTime      time.Time `description:"更新时间"`
+}
+
+// 新增章节ticker
+func InsertChapterTicker(tickerInfo *ReportChapterTicker) (lastId int64, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	lastId, err = o.Insert(tickerInfo)
+	return
+}
+
+// GetTickerListByReportChapterId 根据章节ID获取ticker列表
+func GetTickerListByReportChapterId(reportChapterId int) (list []*ReportChapterTicker, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM report_chapter_ticker WHERE report_chapter_id = ? ORDER BY sort ASC `
+	_, err = o.Raw(sql, reportChapterId).QueryRows(&list)
+
+	return
+}
+
+// ClearReportChapterTicker 清空章节ticker
+func ClearReportChapterTicker(reportChapterId int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` DELETE FROM report_chapter_ticker WHERE report_chapter_id = ? `
+	_, err = o.Raw(sql, reportChapterId).Exec()
+
+	return
+}
+
+// DailyBaseColumn 基础列
+type DailyBaseColumn struct {
+	BaseColumnId          int       `orm:"column(base_column_id)" description:"主键ID"`
+	TableName             string    `description:"表名"`
+	BaseColumnName        string    `description:"列名称"`
+	BaseColumnChapterType string    `description:"列类型"`
+	BaseColumnTicker      string    `description:"指标"`
+	Enabled               int       `description:"状态"`
+	CreatedTime           time.Time `description:"创建时间"`
+	LastUpdatedTime       time.Time `description:"更新时间"`
+	Freq                  string    `description:"频率 D-日度 W-周度 M-月度"`
+	Catalog               string    `description:"分类名称"`
+	ReportChapterTypeId   int       `description:"分类ID"`
+	Selected              int       `description:"选中状态 0-未选中 1-已选中"`
+}
+
+// GetDailyBaseColumnList 获取基础列列表
+func GetDailyBaseColumnList(keyword string, typeId int) (list []*DailyBaseColumn, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM daily_base_column WHERE 1 = 1 `
+	pars := make([]interface{}, 0)
+	if keyword != "" {
+		keyword = "%" + keyword + "%"
+		pars = append(pars, keyword)
+		sql += ` AND base_column_name like ? `
+	}
+	pars = append(pars, typeId)
+	sql += ` AND report_chapter_type_id = ? `
+	_, err = o.Raw(sql, pars).QueryRows(&list)
+
+	return
+}

+ 385 - 0
models/report_chapter_type.go

@@ -0,0 +1,385 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"hongze/hongze_ETA_mobile_api/utils"
+	"time"
+)
+
+type ReportChapterType struct {
+	ReportChapterTypeId    int       `orm:"column(report_chapter_type_id);pk" description:"报告章节类型id"`
+	ReportChapterTypeKey   string    `description:"章节key"`
+	ReportChapterTypeThumb string    `description:"H5展示的图片"`
+	BannerUrl              string    `description:"banner显示图片"`
+	ReportChapterTypeName  string    `description:"报告章节类型名称"`
+	Sort                   int       `description:"排序字段"`
+	Enabled                int       `description:"启禁用状态"`
+	CreatedTime            time.Time `description:"创建时间"`
+	LastUpdatedTime        time.Time `description:"更新时间"`
+	ResearchType           string    `description:"研报类型"`
+	SelectedImage          string    `description:"选中时的图片"`
+	UnselectedImage        string    `description:"没选中时的图片"`
+	PcSelectedImage        string    `description:"PC-选中的图片"`
+	PcUnselectedImage      string    `description:"PC-未选中的图片"`
+	EditImgUrl             string    `description:"管理后台编辑时选用的图"`
+	TickerTitle            string    `description:"指标列的标题"`
+	IsShow                 int       `description:"是否显示(研报小程序端根据此字段判断)"`
+	PauseStartTime         string    `description:"暂停开始日期"`
+	PauseEndTime           string    `description:"暂停结束日期"`
+	IsSet                  int       `description:"是否设置:0为设置,1已设置"`
+	YbIconUrl              string    `description:"研报小程序icon"`
+	YbBottomIcon           string    `description:"研报小程序详情底部icon"`
+}
+
+func (item *ReportChapterType) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(item)
+	if err != nil {
+		return
+	}
+	item.ReportChapterTypeId = int(id)
+	return
+}
+
+func (item *ReportChapterType) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(item, cols...)
+	return
+}
+
+// DeleteReportChapterType 删除章节类型及相关权限
+func DeleteReportChapterType(typeId int, reportType string) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 删除章节类型
+	sql := `DELETE FROM report_chapter_type WHERE report_chapter_type_id = ? LIMIT 1`
+	_, err = to.Raw(sql, typeId).Exec()
+
+	// 删除章节类型权限
+	sql = `DELETE FROM report_chapter_type_permission WHERE report_chapter_type_id = ?`
+	_, err = to.Raw(sql, typeId).Exec()
+
+	// 周报-删除实际权限
+	if reportType == utils.REPORT_TYPE_WEEK {
+		sql = `DELETE FROM chart_permission_chapter_mapping WHERE report_chapter_type_id = ? AND research_type = ?`
+		_, err = to.Raw(sql, typeId, reportType).Exec()
+	}
+	return
+}
+
+// GetReportChapterTypeById 获取章节类型
+func GetReportChapterTypeById(reportChapterTypeId int) (item *ReportChapterType, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM report_chapter_type WHERE report_chapter_type_id = ? `
+	err = o.Raw(sql, reportChapterTypeId).QueryRow(&item)
+	return
+}
+
+// GetReportChapterTypeList 获取章节类型列表
+func GetReportChapterTypeList() (list []*ReportChapterType, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM report_chapter_type WHERE enabled = 1 `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// GetReportChapterTypeListByResearchType 通过报告类型获取章节类型列表
+func GetReportChapterTypeListByResearchType(researchType string) (list []*ReportChapterType, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM report_chapter_type WHERE research_type = ? AND enabled = 1`
+	_, err = o.Raw(sql, researchType).QueryRows(&list)
+	return
+}
+
+// GetAllReportChapterTypeListByResearchType 通过报告类型获取章节类型列表
+func GetAllReportChapterTypeListByResearchType(researchType string) (list []*ReportChapterType, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM report_chapter_type WHERE research_type = ?`
+	_, err = o.Raw(sql, researchType).QueryRows(&list)
+	return
+}
+
+// GetAllReportChapterTypeList 通过传入的条件获取所有的章节类型列表
+func GetAllReportChapterTypeList(condition string, pars []interface{}) (list []*ReportChapterType, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM report_chapter_type WHERE 1=1 `
+	sql += condition
+	_, err = o.Raw(sql, pars).QueryRows(&list)
+	return
+}
+
+// GetEnableReportChapterTypeList 获取未暂停的章节类型列表
+func GetEnableReportChapterTypeList(researchType string) (list []*ReportChapterType, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT
+				*
+			FROM
+				report_chapter_type
+			WHERE
+				enabled = 1
+			AND research_type = ?
+			AND (
+				(
+					ISNULL(pause_start_time) AND ISNULL(pause_end_time)
+				)
+				OR (
+					NOW() NOT BETWEEN pause_start_time AND pause_end_time
+				)
+			) `
+	_, err = o.Raw(sql, researchType).QueryRows(&list)
+	return
+}
+
+// 晨报周报暂停时间
+type DayWeekReportPauseTime struct {
+	ResearchType   string `description:"报告类型 day; week;"`
+	PauseStartTime string `description:"暂停开始时间"`
+	PauseEndTime   string `description:"暂停结束时间"`
+}
+
+// GetDayWeekReportPauseTimeList 获取晨报周报暂停时间
+func GetDayWeekReportPauseTimeList() (list []*DayWeekReportPauseTime, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT research_type, MAX(a.pause_start_time) AS pause_start_time, MAX(a.pause_end_time) AS pause_end_time FROM report_chapter_type AS a WHERE a.is_set = 1 GROUP BY a.research_type`
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// SetDayWeekReportUpdateRuleReq 设置章节类型的暂停时间请求体
+type SetDayWeekReportUpdateRuleReq struct {
+	ResearchType string                    `description:"报告类型 day; week;"`
+	List         []DayWeekReportUpdateRule `description:"暂停规则"`
+}
+
+type DayWeekReportUpdateRule struct {
+	ReportChapterTypeId int    `description:"章节类型ID"`
+	PauseStartTime      string `description:"暂停开始时间"`
+	PauseEndTime        string `description:"暂停结束时间"`
+}
+
+// SetDayWeekReportEnableUpdateRuleReq 设置章节类型的永久暂停请求体
+type SetDayWeekReportEnableUpdateRuleReq struct {
+	DayReportChapterTypeId  string `description:"章节类型ID 英文逗号拼接"`
+	WeekReportChapterTypeId string `description:"章节类型ID 英文逗号拼接"`
+}
+
+// ResetDayWeekReportUpdateRule 重置章节类型的暂停时间
+func ResetDayWeekReportUpdateRule(researchType string) (err error) {
+	o := orm.NewOrm()
+	sql := ` UPDATE report_chapter_type SET pause_start_time = null, pause_end_time = null, is_set = 0 WHERE research_type = ?`
+	_, err = o.Raw(sql, researchType).Exec()
+	return
+}
+
+// SetDayWeekReportUpdateRule 设置章节类型的暂停时间
+func SetDayWeekReportUpdateRule(researchType string, list []DayWeekReportUpdateRule) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 先将所有品种的状态变更为启用
+	sql := ` UPDATE report_chapter_type SET pause_start_time = null, pause_end_time = null, is_set = 0 WHERE research_type = ?`
+	_, err = to.Raw(sql, researchType).Exec()
+	if err != nil {
+		return
+	}
+	for _, v := range list {
+		// 时间异常的话,给过滤
+		if v.PauseStartTime == `` || v.PauseEndTime == `` {
+			continue
+		}
+		tmpSql := ` UPDATE report_chapter_type SET pause_start_time = ?, pause_end_time = ?, is_set = 1 WHERE research_type = ? AND report_chapter_type_id = ? `
+		_, err = to.Raw(tmpSql, v.PauseStartTime, v.PauseEndTime, researchType, v.ReportChapterTypeId).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+// SetDayWeekReportEnableUpdateRule 设置章节类型的禁用状态
+func SetDayWeekReportEnableUpdateRule(dayReportChapterTypeIdList, weekReportChapterTypeIdList []string) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	{
+		researchType := `day`
+		// 先将所有品种的状态变更为启用
+		sql := ` UPDATE report_chapter_type SET enabled = 1 WHERE research_type = ?`
+		_, err = to.Raw(sql, researchType).Exec()
+		if err != nil {
+			return
+		}
+
+		// 然后将需要的品种的状态变更为禁用
+		num := len(dayReportChapterTypeIdList)
+		if num > 0 {
+			sql = ` UPDATE report_chapter_type SET pause_start_time = null, pause_end_time = null, is_set = 0 , enabled = 0 WHERE research_type = ? AND report_chapter_type_id IN (` + utils.GetOrmInReplace(num) + `) `
+			_, err = to.Raw(sql, researchType, dayReportChapterTypeIdList).Exec()
+		}
+	}
+
+	{
+		researchType := `week`
+		// 先将所有品种的状态变更为启用
+		sql := ` UPDATE report_chapter_type SET enabled = 1 WHERE research_type = ?`
+		_, err = to.Raw(sql, researchType).Exec()
+		if err != nil {
+			return
+		}
+
+		// 然后将需要的品种的状态变更为禁用
+		num := len(weekReportChapterTypeIdList)
+		if num > 0 {
+			sql = ` UPDATE report_chapter_type SET pause_start_time = null, pause_end_time = null, is_set = 0 , enabled = 0 WHERE research_type = ? AND report_chapter_type_id IN (` + utils.GetOrmInReplace(num) + `) `
+			_, err = to.Raw(sql, researchType, weekReportChapterTypeIdList).Exec()
+		}
+	}
+	return
+}
+
+// StopUpdateReportChapterTypeResp 停止更新的报告分类列表
+type StopUpdateReportChapterTypeResp struct {
+	StopDay     []*ReportChapterType `description:"暂时停更晨报"`
+	StopWeek    []*ReportChapterType `description:"暂时停更周报"`
+	DisableDay  []*ReportChapterType `description:"永久停更晨报"`
+	DisableWeek []*ReportChapterType `description:"永久停更周报"`
+}
+
+// GetStopUpdateReportChapterTypeListByResearchType 获取暂停更新章节类型列表
+func GetStopUpdateReportChapterTypeListByResearchType() (list []*ReportChapterType, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM report_chapter_type WHERE is_set = 1 AND pause_end_time >= ? AND enabled = 1 `
+	_, err = o.Raw(sql, time.Now().Format(utils.FormatDate)).QueryRows(&list)
+	return
+}
+
+// GetDisableUpdateReportChapterTypeListByResearchType 获取停止更新的章节类型列表
+func GetDisableUpdateReportChapterTypeListByResearchType() (list []*ReportChapterType, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM report_chapter_type WHERE enabled = 0`
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// UpdateReportChapterTypeResp 停止更新的报告分类列表
+type UpdateReportChapterTypeResp struct {
+	Day  []*ReportChapterType `description:"所有晨报品种"`
+	Week []*ReportChapterType `description:"所有周报品种"`
+}
+
+type ReportChapterTypePageListResp struct {
+	List   []*ReportChapterTypeListItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+// GetReportChapterTypeCount 获取章节类型总数
+func GetReportChapterTypeCount(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(1) FROM report_chapter_type WHERE 1 = 1 `
+	sql += condition
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetReportChapterTypeList 获取章节类型列表
+func GetReportChapterTypePageList(condition string, pars []interface{}, startSize, pageSize int) (list []*ReportChapterType, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM report_chapter_type WHERE 1 = 1 `
+	sql += condition
+	sql += ` ORDER BY sort ASC, created_time DESC`
+	sql += ` LIMIT ?,?`
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&list)
+	return
+}
+
+// ReportChapterTypeListItem 章节类型列表信息
+type ReportChapterTypeListItem struct {
+	ReportChapterTypeId   int    `description:"报告章节类型id"`
+	ReportChapterTypeName string `description:"报告章节类型名称"`
+	Sort                  int    `description:"排序字段"`
+	CreatedTime           string `description:"创建时间"`
+	ResearchType          string `description:"研报类型"`
+	SelectedImage         string `description:"选中时的图片"`
+	UnselectedImage       string `description:"没选中时的图片"`
+	WordsImage            string `description:"带字的icon"`
+	EditImgUrl            string `description:"管理后台编辑时选用的图"`
+	IsShow                int    `description:"显示隐藏: 1-显示; 0-隐藏"`
+}
+
+// ReportChapterTypeAddReq 新增章节类型请求体
+type ReportChapterTypeAddReq struct {
+	ReportChapterTypeName string `description:"报告章节类型名称"`
+	Sort                  int    `description:"排序字段"`
+	ResearchType          string `description:"研报类型"`
+	SelectedImage         string `description:"选中时的icon"`
+	UnselectedImage       string `description:"未选中时的icon"`
+	WordsImage            string `description:"带字的icon"`
+	EditImgUrl            string `description:"管理后台编辑时选用的图"`
+	IsShow                int    `description:"显示隐藏: 1-显示; 0-隐藏"`
+}
+
+// ReportChapterTypeEditReq 编辑章节类型请求体
+type ReportChapterTypeEditReq struct {
+	ReportChapterTypeId   int    `description:"报告章节类型id"`
+	ReportChapterTypeName string `description:"报告章节类型名称"`
+	Sort                  int    `description:"排序字段"`
+	ResearchType          string `description:"研报类型"`
+	SelectedImage         string `description:"选中时的icon"`
+	UnselectedImage       string `description:"未选中时的icon"`
+	WordsImage            string `description:"带字的icon"`
+	EditImgUrl            string `description:"管理后台编辑时选用的图"`
+	IsShow                int    `description:"显示隐藏: 1-显示; 0-隐藏"`
+}
+
+// ReportChapterTypeDelReq 删除章节类型请求体
+type ReportChapterTypeDelReq struct {
+	ReportChapterTypeId int `description:"报告章节类型id"`
+}
+
+// ReportChapterTypeAuthSettingReq 章节类型权限配置请求体
+type ReportChapterTypeAuthSettingReq struct {
+	ReportChapterTypeId   int   `description:"章节类型ID"`
+	ChartPermissionIdList []int `description:"权限id数组"`
+}
+
+// GetReportChapterTypeByCondition 获取章节类型
+func GetReportChapterTypeByCondition(condition string, pars []interface{}) (item *ReportChapterType, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM report_chapter_type WHERE 1 = 1 `
+	sql += condition
+	sql += ` LIMIT 1`
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}

+ 82 - 0
models/report_chapter_type_permission.go

@@ -0,0 +1,82 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"hongze/hongze_ETA_mobile_api/utils"
+	"time"
+)
+
+type ReportChapterTypePermission struct {
+	Id                    int       `orm:"column(id);pk" description:"主键ID"`
+	ReportChapterTypeId   int       `description:"报告章节类型ID"`
+	ReportChapterTypeName string    `description:"章节名称"`
+	ChartPermissionId     int       `description:"大分类ID"`
+	PermissionName        string    `description:"权限名称"`
+	ResearchType          string    `description:"研报类型"`
+	CreatedTime           time.Time `description:"创建时间"`
+}
+
+// AddChapterTypePermission 新增章节类型权限
+func AddChapterTypePermission(item *ReportChapterTypePermission) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+// GetChapterTypePermissionByTypeIdAndResearchType 根据章节类型ID及研报类型获取章节类型权限列表
+func GetChapterTypePermissionByTypeIdAndResearchType(typeId int, researchType string) (list []*ReportChapterTypePermission, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM report_chapter_type_permission WHERE report_chapter_type_id = ? AND research_type = ? ORDER BY chart_permission_id ASC `
+	_, err = o.Raw(sql, typeId, researchType).QueryRows(&list)
+	return
+}
+
+// SetReportChapterTypePermission 设置报告章节类型权限
+func SetReportChapterTypePermission(chapterTypeId int, researchType string, newPermissions []*ReportChapterTypePermission, newWeekPermissions []*ChartPermissionChapterMapping) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 删除原章节类型权限
+	sql := `DELETE FROM report_chapter_type_permission WHERE report_chapter_type_id = ? AND research_type = ?`
+	_, err = to.Raw(sql, chapterTypeId, researchType).Exec()
+	if err != nil {
+		return
+	}
+
+	// 新增章节类型权限
+	if len(newPermissions) > 0 {
+		_, err = to.InsertMulti(len(newPermissions), newPermissions)
+		if err != nil {
+			return
+		}
+	}
+
+	// 周报章节调整chart_permission_chapter_mapping表
+	if researchType == utils.REPORT_TYPE_WEEK {
+		// 删除原权限
+		sql = `DELETE FROM chart_permission_chapter_mapping WHERE report_chapter_type_id = ? AND research_type = ?`
+		_, err = to.Raw(sql, chapterTypeId, researchType).Exec()
+		if err != nil {
+			return
+		}
+
+		// 新增权限
+		if len(newWeekPermissions) > 0 {
+			_, err = to.InsertMulti(len(newWeekPermissions), newWeekPermissions)
+			if err != nil {
+				return
+			}
+		}
+	}
+	return
+}

+ 302 - 0
models/research_report.go

@@ -0,0 +1,302 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"hongze/hongze_ETA_mobile_api/utils"
+	"strconv"
+	"time"
+)
+
+// ResearchReport 研究报告表(晨报、周报等)结构体
+type ResearchReport struct {
+	ResearchReportId    int       `orm:"column(research_report_id);pk" description:"研究报告id"`
+	ResearchReportName  string    `description:"研究报告名称"`
+	ResearchReportTitle string    `description:"研究报告标题"`
+	ResearchReportImg   string    `description:"报告缩略图URL"`
+	ResearchReportDate  time.Time `description:"报告日期"`
+	Type                string    `description:"报告类型,枚举值:day 晨报  week 周报 twoweek双周报 month 月报;默认:day"`
+	Author              string    `description:"作者"`
+	ReportVariety       string    `description:"研究报告的品种,双周报和月报有标识"`
+	IsHasMenu           int8      `description:"报告是否含有目录"`
+	IsSendedMsg         int8      `description:"是否发送过模板消息"`
+	Periods             int       `description:"期数"`
+	Status              string    `description:"状态,draft:草稿,"`
+	Enabled             int8      `description:"报告状态"`
+	CreatedTime         string    `description:"创建时间"`
+	LastUpdatedTime     time.Time `description:"最近一次更新时间"`
+	Viewers             int       `description:"H5观看用户数"`
+}
+
+// GetResearchReportListByIds 根据报告id集合获取报告数据列表
+func GetResearchReportListByIds(researchReportIds string) (list []*ResearchReport, err error) {
+	if researchReportIds == "" {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `select * from research_report where research_report_id in (` + researchReportIds + `)`
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// Update 更新数据
+func (researchReport *ResearchReport) Update(updateCols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(researchReport, updateCols...)
+	return
+}
+
+type ResearchReportList struct {
+	ResearchReportId    int       `orm:"column(research_report_id);pk" description:"研究报告id"`
+	ResearchReportName  string    `description:"研究报告名称"`
+	ResearchReportTitle string    `description:"研究报告标题"`
+	ResearchReportImg   string    `description:"报告缩略图URL"`
+	ResearchReportDate  time.Time `description:"报告日期"`
+	Type                string    `description:"报告类型,枚举值:day 晨报  week 周报 twoweek双周报 month 月报;默认:day"`
+	Author              string    `description:"作者"`
+	ReportVariety       string    `description:"研究报告的品种,双周报和月报有标识"`
+	IsHasMenu           int8      `description:"报告是否含有目录"`
+	IsSendedMsg         int8      `description:"是否发送过模板消息"`
+	Periods             int       `description:"期数"`
+	Status              string    `description:"状态,draft:草稿,"`
+	Enabled             int8      `description:"报告状态"`
+	CreatedTime         string    `description:"创建时间"`
+	LastUpdatedTime     time.Time `description:"最近一次更新时间"`
+	Viewers             int       `description:"H5观看用户数"`
+	LinkUrl             string    `description:"报告阅读地址"`
+}
+
+// GetResearchReportList 获取报告列表
+func GetResearchReportList(condition string, pars []interface{}, startSize, pageSize int) (total int, list []*ResearchReportList, err error) {
+	o := orm.NewOrm()
+
+	sql := `select * from research_report where enabled = 1 `
+	sql += condition
+	sql += ` order by research_report_date desc,created_time desc `
+
+	totalSql := `select count(1) total from (` + sql + `) z `
+	err = o.Raw(totalSql, pars).QueryRow(&total)
+	if err != nil {
+		return
+	}
+	sql += ` LIMIT ?,? `
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&list)
+
+	return
+}
+
+// 获取今年报告
+func GetMigrateReportList() (list []*ResearchReport, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT
+				*
+			FROM
+				research_report AS a
+			WHERE
+				a.enabled = 1
+			AND a.status = "report"
+			AND a.research_report_date >= "2022-01-01"
+			ORDER BY a.research_report_date ASC `
+	_, err = o.Raw(sql).QueryRows(&list)
+
+	return
+}
+
+// ResearchReport 研究报告表(晨报、周报等)结构体
+type ResearchReportType struct {
+	ResearchReportTypeId    int       `orm:"column(research_report_type_id);pk" description:"报告章节ID"`
+	ResearchReportId        int       `description:"报告ID"`
+	TypeId                  int       `description:"分类ID"`
+	Edit                    int       `description:"是否编辑过"`
+	Trend                   string    `description:"趋势观点"`
+	ResearchReportTypeTitle string    `description:"报告标题"`
+	CreatedTime             string    `description:"创建时间"`
+	LastUpdatedTime         time.Time `description:"最近一次更新时间"`
+}
+
+type ResearchReportTypeContent struct {
+	ResearchReportTypeContentId int       `orm:"column(research_report_type_content_id);pk" description:"章节内容ID"`
+	ResearchReportTypeId        int       `description:"报告章节ID"`
+	Sort                        int       `description:"排序"`
+	ContentType                 string    `description:"内容分类类型"`
+	Content                     string    `description:"内容"`
+	ImgUrl                      string    `description:"图片路径"`
+	CreatedTime                 time.Time `description:"创建时间"`
+	LastUpdatedTime             time.Time `description:"最近一次更新时间"`
+}
+
+type ResearchReportTypeTicker struct {
+	ResearchReportTypeTickerId int       `orm:"column(research_report_type_ticker_id);pk" description:"章节tickerID"`
+	ResearchReportTypeId       int       `description:"报告章节ID"`
+	Sort                       int       `description:"排序"`
+	Ticker                     string    `description:"指标的ticker"`
+	CreatedTime                time.Time `description:"创建时间"`
+	LastUpdatedTime            time.Time `description:"最近一次更新时间"`
+}
+
+func GetResearchReportTypeList(researchReportId int) (list []*ResearchReportType, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM research_report_type WHERE research_report_id = ? ORDER BY created_time ASC `
+	_, err = o.Raw(sql, researchReportId).QueryRows(&list)
+	return
+}
+
+func GetResearchReportTypeListByReportIds(reportIds string) (list []*ResearchReportType, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM research_report_type WHERE research_report_id IN (` + reportIds + `) ORDER BY created_time ASC,research_report_type_id ASC `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+func GetResearchReportTypeContentList(researchReportTypeId int) (list []*ResearchReportTypeContent, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM research_report_type_content WHERE research_report_type_id = ? ORDER BY sort ASC `
+	_, err = o.Raw(sql, researchReportTypeId).QueryRows(&list)
+	return
+}
+
+func GetResearchReportTypeContentListByReportTypeIds(reportTypeIds string) (list []*ResearchReportTypeContent, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM research_report_type_content WHERE research_report_type_id IN (` + reportTypeIds + `) ORDER BY research_report_type_id ASC,sort ASC `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+func GetResearchReportTypeTickerList(researchReportTypeId int) (list []*ResearchReportTypeTicker, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM research_report_type_ticker WHERE research_report_type_id = ? ORDER BY sort ASC `
+	_, err = o.Raw(sql, researchReportTypeId).QueryRows(&list)
+	return
+}
+
+func GetResearchReportTypeTickerListByReportTypeIds(reportTypeIds string) (list []*ResearchReportTypeTicker, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM research_report_type_ticker WHERE research_report_type_id IN (` + reportTypeIds + `) ORDER BY research_report_type_id ASC,sort ASC `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+type CreateDayWeekReport struct {
+	Report      *Report
+	ChapterList []*CreateDayWeekReportChapter
+}
+
+type CreateDayWeekReportChapter struct {
+	Chapter    *ReportChapter
+	TickerList []*ReportChapterTicker
+}
+
+// 新增迁移晨周报
+func CreateMigrateNewDayWeekReport(newDayWeekReport *CreateDayWeekReport) (newReportId int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 新增报告
+	reportId, err := to.Insert(newDayWeekReport.Report)
+	if err != nil {
+		fmt.Println("InsertReportErr:" + err.Error())
+		return
+	}
+	// 新增章节
+	for _, chapter := range newDayWeekReport.ChapterList {
+		chapter.Chapter.ReportId = int(reportId)
+		lastChapterId, tmpErr := to.Insert(chapter.Chapter)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		// 新增晨报ticker
+		for _, ticker := range chapter.TickerList {
+			ticker.ReportChapterId = int(lastChapterId)
+			if _, tmpErr = to.Insert(ticker); tmpErr != nil {
+				err = tmpErr
+				return
+			}
+		}
+	}
+	// 修改报告code
+	reportCode := utils.MD5(strconv.Itoa(int(reportId)))
+	sql := `UPDATE report SET report_code = ? WHERE id = ? `
+	if _, err = to.Raw(sql, reportCode, reportId).Exec(); err != nil {
+		fmt.Println("UpdateReportCodeErr:" + err.Error())
+	}
+	newReportId = int(reportId)
+
+	return
+}
+
+// 新增迁移其他报告
+func CreateMigrateNewOtherReport(reportInfo *Report, mappingList []*ChartPermissionChapterMapping) (newReportId int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 新增报告
+	reportId, err := to.Insert(reportInfo)
+	if err != nil {
+		fmt.Println("InsertReportErr:" + err.Error())
+		return
+	}
+	// 修改报告code
+	reportCode := utils.MD5(strconv.Itoa(int(reportId)))
+	sql := `UPDATE report SET report_code = ? WHERE id = ? `
+	if _, err = to.Raw(sql, reportCode, reportId).Exec(); err != nil {
+		fmt.Println("UpdateReportCodeErr:" + err.Error())
+		return
+	}
+
+	// 新增权限
+	newReportId = int(reportId)
+	if len(mappingList) > 0 {
+		r := orm.NewOrm()
+		for _, mapping := range mappingList {
+			sql := ` INSERT INTO chart_permission_chapter_mapping (chart_permission_id, report_chapter_type_id,research_type) VALUES(?,?,?) `
+			if _, err = r.Raw(sql, mapping.ChartPermissionId, newReportId, "rddp").Exec(); err != nil {
+				fmt.Println("InsertChartPermissionErr:" + err.Error())
+				return
+			}
+		}
+	}
+
+	return
+}
+
+type ChartPermissionChapterMapping struct {
+	Id                  int    `orm:"column(id);pk"`
+	ChartPermissionId   int    `description:"权限ID"`
+	ReportChapterTypeId int    `description:"report_chapter_type表主键id或research_report表主键id或tactic表主键id"`
+	ResearchType        string `description:"报告类型 week;two_week;tactic;month;other;rddp; "`
+}
+
+func GetChapterPermissionMappingByCondition(reportChapterTypeId int, researchType string) (list []*ChartPermissionChapterMapping, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_permission_chapter_mapping WHERE report_chapter_type_id = ? AND research_type = ? `
+	_, err = o.Raw(sql, reportChapterTypeId, researchType).QueryRows(&list)
+	return
+}
+
+func GetChapterPermissionMappingByResearchReportIds(researchReportIds string) (list []*ChartPermissionChapterMapping, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_permission_chapter_mapping WHERE report_chapter_type_id IN (` + researchReportIds + `) AND research_type != "rddp" ORDER BY report_chapter_type_id ASC `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}

+ 35 - 0
models/search_key_word.go

@@ -0,0 +1,35 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// SearchKeyWord 搜索关键词
+type SearchKeyWord struct {
+	SearchKeyWordId     int       `orm:"column(search_key_word_id)" description:"主键ID"`
+	KeyWord             string    `description:"关键词"`
+	From                string    `description:"来源,在什么地方筛选"`
+	CreatedTime         time.Time `description:"创建时间"`
+	LastUpdatedTime     time.Time `description:"更新时间"`
+	TeleconferenceImage string    `description:"电话会对应的类型图片"`
+	BannerImage         string    `description:"Banner图"`
+}
+
+// AddTrendTagKeyWord 新增趋势标签关键词
+func AddTrendTagKeyWord(trend string) (err error) {
+	o := orm.NewOrm()
+	sql := " REPLACE INTO search_key_word (`key_word`,`from`) values (?,'trend') "
+	_, err = o.Raw(sql, trend).Exec()
+
+	return
+}
+
+// GetKeyWordListByFrom 根据来源获取搜索关键词列表
+func GetKeyWordListByFrom(from string) (list []*SearchKeyWord, err error) {
+	o := orm.NewOrm()
+	sql := " SELECT * FROM search_key_word WHERE `from` = ? ORDER BY created_time ASC "
+	_, err = o.Raw(sql, from).QueryRows(&list)
+
+	return
+}

+ 454 - 0
models/user_view_history.go

@@ -0,0 +1,454 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"hongze/hongze_ETA_mobile_api/utils"
+	"time"
+)
+
+type UserViewHistory struct {
+	ViewHistoryId        int       `orm:"column(id);pk"`
+	UserId               int       `description:"用户id"`
+	Mobile               string    `description:"手机号"`
+	Email                string    `description:"邮箱"`
+	RealName             string    `description:"用户实际姓名"`
+	CompanyName          string    `description:"公司名称"`
+	ViewTitle            string    `description:"访问标题"`
+	ViewPage             string    `description:"访问页面"`
+	ReportChapterModule  string    `description:"访问核心观点或者图文逻辑"`
+	CreatedTime          string    `description:"创建时间"`
+	LastUpdatedTime      time.Time `description:"访问历史类型,weekly_report 周报,pdf;默认值:weekly_report"`
+	ResearchReportId     int       `description:"研报id"`
+	ResearchReportTypeId int       `description:"报告章节id,为0时表示查看目录或者首页"`
+}
+
+//根据用户id字符串获取用户的浏览数
+type UserViewTotalSlice struct {
+	UserId      int       `description:"用户id"`
+	Total       int       `description:"总阅读数"`
+	CreatedTime time.Time `description:"用户浏览时间"`
+}
+
+func GetCountUserViewHistoryByUserIds(userIds string) (items []*UserViewTotalSlice, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT count(1) total,user_id,max(created_time) as created_time FROM user_view_history WHERE user_id in (` + userIds + `) group by user_id`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+	//return items2,err
+}
+
+//根据用户手机号字符串获取用户的浏览数
+type UserViewMobileTotalSlice struct {
+	Mobile      string    `description:"用户手机号"`
+	Total       int       `description:"总阅读数"`
+	CreatedTime time.Time `description:"用户浏览时间"`
+}
+
+func GetCountUserViewHistoryByMobiles(mobiles string) (items []*UserViewMobileTotalSlice, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT count(1) total,mobile,max(created_time) as created_time FROM user_view_history WHERE mobile in (` + mobiles + `) group by mobile`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+//根据用户id字符串获取用户的浏览数
+type UserViewEmailTotalSlice struct {
+	Email       string    `description:"用户邮箱"`
+	Total       int       `description:"总阅读数"`
+	CreatedTime time.Time `description:"用户浏览时间"`
+}
+
+func GetCountUserViewHistoryByEmails(emails string) (items []*UserViewEmailTotalSlice, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT count(1) total,email,max(created_time) as created_time FROM user_view_history WHERE email in (` + emails + `) group by email`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+	//return items2,err
+}
+
+func GetCountCygxArticleHistoryRecordByMobiles(mobiles string) (items []*UserViewMobileTotalSlice, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT count(1) total,h.mobile,max(h.create_time) as created_time FROM cygx_article_history_record_all  AS h  INNER JOIN cygx_article  AS art  ON  art.article_id = h.article_id  WHERE h.mobile in (` + mobiles + `) AND h.is_del = 0  AND h.company_id != 16 group by h.mobile`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+func GetCountCygxArticleHistoryRecordByEmails(emails string) (items []*UserViewEmailTotalSlice, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT count(1) total,h.email,max(h.create_time) as created_time FROM cygx_article_history_record_all AS h  INNER JOIN cygx_article  AS art  ON  art.article_id = h.article_id  WHERE h.email in (` + emails + `) AND h.is_del = 0 AND h.company_id != 16 group by email`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// CompanyLastViewSlice 根据手机号获取客户的最新浏览时间
+type CompanyLastViewSlice struct {
+	CompanyId int       `description:"客户id"`
+	ViewTime  time.Time `description:"用户浏览时间"`
+}
+
+// GetLastUserViewHistoryByCompanyIdsMobile 根据手机号获取客户的最新浏览时间
+func GetLastUserViewHistoryByCompanyIdsMobile(companyIds string) (items []*CompanyLastViewSlice, err error) {
+	today := time.Now().Format(utils.FormatDate)
+	o := orm.NewOrm()
+	sql := `SELECT
+	a.company_id ,max(b.created_time) view_time
+FROM
+	wx_user a
+	JOIN user_view_history b ON a.mobile = b.mobile 
+WHERE
+	a.company_id IN ( ` + companyIds + ` )  and b.mobile !="" and b.created_time>=? GROUP BY company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// GetLastUserViewHistoryByCompanyIdsEmail 根据邮箱获取客户的最新浏览时间
+func GetLastUserViewHistoryByCompanyIdsEmail(companyIds string) (items []*CompanyLastViewSlice, err error) {
+	today := time.Now().Format(utils.FormatDate)
+	o := orm.NewOrm()
+	//	sql := `SELECT
+	//	a.company_id ,max(b.created_time) view_time
+	//FROM
+	//	wx_user a
+	//	JOIN user_view_history b ON a.email = b.email
+	//WHERE a.company_id IN ( ` + companyIds + ` ) and b.email !="" and b.mobile=""  and b.created_time>=?  GROUP BY company_id`
+
+	sql := `SELECT
+	a.company_id ,max(b.created_time) view_time FROM wx_user a
+	JOIN user_view_history b ON a.email = b.email 
+WHERE b.email !="" and b.mobile=""  and b.created_time>=?  GROUP BY company_id`
+
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// GetLastAdvisoryArticleViewRecordByCompanyIdsMobile 根据手机号获取客户的最新浏览时间
+func GetLastAdvisoryArticleViewRecordByCompanyIdsMobile(companyIds string) (items []*CompanyLastViewSlice, err error) {
+	today := time.Now().Format(utils.FormatDate)
+	o := orm.NewOrm()
+	sql := `SELECT
+	a.company_id ,max(b.create_time) view_time
+FROM
+	wx_user a
+	JOIN advisory_user_chart_article_record b ON a.mobile = b.mobile 
+WHERE
+	a.company_id IN ( ` + companyIds + ` )  and b.mobile !="" and b.create_time>=? GROUP BY company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// GetLastAdvisoryArticleViewRecordByCompanyIdsEmail 根据邮箱获取客户的最新浏览时间
+func GetLastAdvisoryArticleViewRecordByCompanyIdsEmail(companyIds string) (items []*CompanyLastViewSlice, err error) {
+	today := time.Now().Format(utils.FormatDate)
+	o := orm.NewOrm()
+	sql := `SELECT
+	a.company_id ,max(b.create_time) view_time
+FROM
+	wx_user a
+	JOIN advisory_user_chart_article_record b ON a.email = b.email 
+WHERE a.company_id IN ( ` + companyIds + ` ) and b.email !="" and b.mobile=""  and b.create_time>=?  GROUP BY company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// GetLastCygxArticleViewRecordByCompanyIdsMobile 根据手机号获取客户的最新浏览时间
+func GetLastCygxArticleViewRecordByCompanyIdsMobile(companyIds string) (items []*CompanyLastViewSlice, err error) {
+	o := orm.NewOrm()
+	//dataName := ""
+	//if utils.RunMode == "debug" {
+	//	dataName = "test_v2_hongze_rddp"
+	//} else {
+	//	dataName = "hongze_rddp"
+	//}
+	sql := `SELECT
+	a.company_id ,max(b.create_time) view_time
+FROM
+	wx_user a
+	JOIN cygx_article_history_record_newpv b ON a.mobile = b.mobile 
+WHERE
+	a.company_id IN ( ` + companyIds + ` )  and b.mobile !="" GROUP BY company_id`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// GetLastCygxArticleViewRecordByCompanyIdsEmail 根据邮箱获取客户的最新浏览时间
+func GetLastCygxArticleViewRecordByCompanyIdsEmail(companyIds string) (items []*CompanyLastViewSlice, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+	a.company_id ,max(b.create_time) view_time
+FROM
+	wx_user a
+	JOIN cygx_article_history_record_newpv b ON a.email = b.email 
+WHERE a.company_id IN ( ` + companyIds + ` ) and b.email !="" and b.mobile=""   GROUP BY company_id`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// GetLastReportViewRecordByCompanyIdsMobile 根据手机号获取客户的最新浏览时间
+func GetLastReportViewRecordByCompanyIdsMobile(companyIds string) (items []*CompanyLastViewSlice, err error) {
+	today := time.Now().Format(utils.FormatDate)
+	o := orm.NewOrm()
+	dataName := ""
+	if utils.RunMode == "debug" {
+		dataName = "test_v2_hongze_rddp"
+	} else {
+		dataName = "hongze_rddp"
+	}
+	sql := `SELECT
+	a.company_id ,max(b.create_time) view_time
+FROM
+	wx_user a
+	JOIN ` + dataName + `.report_view_record b ON a.mobile = b.mobile 
+WHERE
+	a.company_id IN ( ` + companyIds + ` )  and b.mobile !="" and b.create_time>=? GROUP BY company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// GetLastReportViewRecordByCompanyIdsEmail 根据邮箱获取客户的最新浏览时间
+func GetLastReportViewRecordByCompanyIdsEmail(companyIds string) (items []*CompanyLastViewSlice, err error) {
+	today := time.Now().Format(utils.FormatDate)
+	o := orm.NewOrm()
+	dataName := ""
+	if utils.RunMode == "debug" {
+		dataName = "test_v2_hongze_rddp"
+	} else {
+		dataName = "hongze_rddp"
+	}
+	sql := `SELECT	a.company_id ,max(b.create_time) view_time FROM wx_user a
+	JOIN ` + dataName + `.report_view_record b ON a.email = b.email 
+WHERE a.company_id IN ( ` + companyIds + ` ) and b.email !="" and b.mobile=""   and b.create_time>=? GROUP BY company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// GetLastUserViewStatisticsByCompanyIdsMobile 根据手机号获取客户的最新浏览时间
+func GetLastUserViewStatisticsByCompanyIdsMobile(companyIds string) (items []*CompanyLastViewSlice, err error) {
+	today := time.Now().Format(utils.FormatDate)
+	o := orm.NewOrm()
+	sql := `SELECT
+	a.company_id ,max(b.last_view_time) view_time
+FROM
+	wx_user a
+	JOIN user_view_statistics b ON a.mobile = b.mobile 
+WHERE
+	a.company_id IN ( ` + companyIds + ` )  and a.mobile !="" and b.mobile !="" and b.date<=? GROUP BY a.company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// GetLastUserViewStatisticsByCompanyIdsEmail 根据邮箱获取客户的最新浏览时间
+func GetLastUserViewStatisticsByCompanyIdsEmail(companyIds string) (items []*CompanyLastViewSlice, err error) {
+	today := time.Now().Format(utils.FormatDate)
+	o := orm.NewOrm()
+	sql := `SELECT
+	a.company_id ,max(b.last_view_time) view_time
+FROM
+	wx_user a
+	JOIN user_view_statistics b ON a.email = b.email 
+WHERE a.company_id IN ( ` + companyIds + ` ) and a.email !="" and b.email !="" and a.mobile="" and b.mobile="" and b.date<=? GROUP BY a.company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// CompanyViewTotalSlice 获取客户的浏览次数
+type CompanyViewTotalSlice struct {
+	CompanyId int `description:"客户id"`
+	ViewTotal int `description:"用户浏览次数"`
+}
+
+// GetCountUserViewHistoryByCompanyIdsMobile 根据手机号获取客户的浏览次数
+func GetCountUserViewHistoryByCompanyIdsMobile(companyIds string) (items []*CompanyViewTotalSlice, err error) {
+	today := time.Now().Format(utils.FormatDate) + " 00:00:00"
+	o := orm.NewOrm()
+	sql := `SELECT
+	a.company_id ,count(1) view_total FROM 	wx_user a
+	JOIN user_view_history b ON a.mobile = b.mobile 
+WHERE 	a.company_id IN ( ` + companyIds + ` )  and b.mobile !="" and b.created_time>=? GROUP BY company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// GetCountUserViewHistoryByCompanyIdsEmail 根据邮箱获取客户的浏览次数
+func GetCountUserViewHistoryByCompanyIdsEmail(companyIds string) (items []*CompanyViewTotalSlice, err error) {
+	today := time.Now().Format(utils.FormatDate) + " 00:00:00"
+	o := orm.NewOrm()
+	//	sql := `SELECT
+	//	a.company_id ,count(1) view_total
+	//FROM
+	//	wx_user a
+	//	JOIN user_view_history b ON a.email = b.email
+	//WHERE a.company_id IN ( ` + companyIds + ` ) and b.email !="" and b.mobile=""  and b.created_time>=? GROUP BY company_id`
+
+	sql := `SELECT a.company_id ,count(1) view_total FROM wx_user a 
+JOIN user_view_history b ON a.email = b.email 
+WHERE  b.email !="" and b.mobile=""  and b.created_time>=? GROUP BY company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// GetCountAdvisoryArticleViewRecordByCompanyIdsMobile 根据手机号获取客户的浏览次数
+func GetCountAdvisoryArticleViewRecordByCompanyIdsMobile(companyIds string) (items []*CompanyViewTotalSlice, err error) {
+	today := time.Now().Format(utils.FormatDate) + " 00:00:00"
+	o := orm.NewOrm()
+	sql := `SELECT
+	a.company_id ,count(1) view_total
+FROM
+	wx_user a
+	JOIN advisory_user_chart_article_record b ON a.mobile = b.mobile 
+WHERE
+	a.company_id IN ( ` + companyIds + ` )  and b.mobile !="" and create_time>=? GROUP BY company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// GetCountAdvisoryArticleViewRecordByCompanyIdsEmail 根据邮箱获取客户的浏览次数
+func GetCountAdvisoryArticleViewRecordByCompanyIdsEmail(companyIds string) (items []*CompanyViewTotalSlice, err error) {
+	today := time.Now().Format(utils.FormatDate) + " 00:00:00"
+	o := orm.NewOrm()
+	sql := `SELECT
+	a.company_id ,count(1) view_total
+FROM
+	wx_user a
+	JOIN advisory_user_chart_article_record b ON a.email = b.email 
+WHERE a.company_id IN ( ` + companyIds + ` ) and b.email !="" and b.mobile="" and create_time>=? GROUP BY company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// GetCountCygxArticleViewRecordByCompanyIdsMobile 根据手机号获取客户的浏览次数
+func GetCountCygxArticleViewRecordByCompanyIdsMobile(companyIds string) (items []*CompanyViewTotalSlice, err error) {
+	o := orm.NewOrm()
+	//dataName := ""
+	//if utils.RunMode == "debug" {
+	//	dataName = "test_v2_hongze_rddp"
+	//} else {
+	//	dataName = "hongze_rddp"
+	//}
+	sql := `SELECT
+	a.company_id ,count(1) view_total
+FROM
+	wx_user a
+	JOIN cygx_article_history_record_newpv b ON a.mobile = b.mobile 
+WHERE
+	a.company_id IN ( ` + companyIds + ` )  and b.mobile !="" GROUP BY company_id`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// GetCountCygxArticleViewRecordByCompanyIdsEmail 根据邮箱获取客户的浏览次数
+func GetCountCygxArticleViewRecordByCompanyIdsEmail(companyIds string) (items []*CompanyViewTotalSlice, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+	a.company_id ,count(1) view_total
+FROM
+	wx_user a
+	JOIN cygx_article_history_record_newpv b ON a.email = b.email 
+WHERE a.company_id IN ( ` + companyIds + ` ) and b.email !="" and b.mobile="" GROUP BY company_id`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// GetCountReportViewRecordByCompanyIdsMobile 根据手机号获取客户的浏览次数
+func GetCountReportViewRecordByCompanyIdsMobile(companyIds string) (items []*CompanyViewTotalSlice, err error) {
+	today := time.Now().Format(utils.FormatDate) + " 00:00:00"
+	o := orm.NewOrm()
+	dataName := ""
+	if utils.RunMode == "debug" {
+		dataName = "test_v2_hongze_rddp"
+	} else {
+		dataName = "hongze_rddp"
+	}
+	sql := `SELECT
+	a.company_id ,count(1) view_total
+FROM
+	wx_user a
+	JOIN ` + dataName + `.report_view_record b ON a.mobile = b.mobile 
+WHERE
+	a.company_id IN ( ` + companyIds + ` )  and b.mobile !="" and create_time>=? GROUP BY company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// GetCountReportViewRecordByCompanyIdsEmail 根据邮箱获取客户的浏览次数
+func GetCountReportViewRecordByCompanyIdsEmail(companyIds string) (items []*CompanyViewTotalSlice, err error) {
+	today := time.Now().Format(utils.FormatDate) + " 00:00:00"
+	o := orm.NewOrm()
+	dataName := ""
+	if utils.RunMode == "debug" {
+		dataName = "test_v2_hongze_rddp"
+	} else {
+		dataName = "hongze_rddp"
+	}
+	sql := `SELECT	a.company_id ,count(1) view_total FROM wx_user a
+	JOIN ` + dataName + `.report_view_record b ON a.email = b.email 
+WHERE a.company_id IN ( ` + companyIds + ` ) and b.email !="" and b.mobile="" and create_time>=? GROUP BY company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// GetUserViewStatisticsByCompanyIdsMobile 根据手机号获取客户的浏览次数
+func GetUserViewStatisticsByCompanyIdsMobile(companyIds string) (items []*CompanyViewTotalSlice, err error) {
+	today := time.Now().Format(utils.FormatDate)
+	o := orm.NewOrm()
+	sql := `SELECT
+	a.company_id ,sum(b.view_num) view_total
+FROM
+	wx_user a
+	JOIN user_view_statistics b ON a.mobile = b.mobile 
+WHERE
+	a.company_id IN ( ` + companyIds + ` )  and a.mobile !="" and b.mobile !="" and date<=? GROUP BY a.company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// GetUserViewStatisticsByCompanyIdsEmail 根据邮箱获取客户的浏览次数
+func GetUserViewStatisticsByCompanyIdsEmail(companyIds string) (items []*CompanyViewTotalSlice, err error) {
+	today := time.Now().Format(utils.FormatDate)
+	o := orm.NewOrm()
+	sql := `SELECT
+	a.company_id ,sum(b.view_num) view_total
+FROM
+	wx_user a
+	JOIN user_view_statistics b ON a.email = b.email 
+WHERE a.company_id IN ( ` + companyIds + ` ) and a.email !="" and b.email !="" and a.mobile="" and b.mobile="" and date<=? GROUP BY a.company_id`
+	_, err = o.Raw(sql, today).QueryRows(&items)
+	return
+}
+
+// UserViewStatisticsInfo 根据用户手机号字符串获取用户的浏览数和最晚阅读次数
+type UserViewStatisticsInfo struct {
+	Mobile       string    `description:"用户手机号"`
+	Total        int       `description:"总阅读数"`
+	LastViewTime time.Time `description:"用户浏览时间"`
+}
+
+// GetUserViewStatisticsByMobile 根据手机号获取联系人的浏览次数
+func GetUserViewStatisticsByMobile(mobile string) (item *UserViewStatisticsInfo, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT mobile,sum(view_num) total,max(last_view_time) last_view_time FROM  user_view_statistics  WHERE mobile = ? `
+	err = o.Raw(sql, mobile).QueryRow(&item)
+	return
+}
+
+type ResearchReportViewPUV struct {
+	ResearchReportId int
+	Pv               int
+	Uv               int
+}
+
+// GetPUVByResearchReportIds 通过报告IDs获取老报告PV、UV
+func GetPUVByResearchReportIds(reportIds string) (list []*ResearchReportViewPUV, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+				research_report_id,
+				COUNT(1) AS pv,
+				COUNT(DISTINCT user_id) AS uv
+			FROM
+				user_view_history
+			WHERE
+				research_report_id IN (` + reportIds +`)
+			GROUP BY
+				research_report_id`
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}

+ 476 - 0
models/wechat_send_msg.go

@@ -0,0 +1,476 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"hongze/hongze_ETA_mobile_api/utils"
+	"strings"
+)
+
+type OpenIdList struct {
+	OpenId string
+	UserId int
+}
+
+func GetOpenIdList() (items []*OpenIdList, err error) {
+	//openIdstr := WxUsersGet()
+	//sql:=` SELECT DISTINCT open_id FROM wx_user AS wu
+	//      INNER JOIN company AS c ON c.company_id = wu.company_id
+	//      INNER JOIN company_product AS d ON c.company_id=d.company_id
+	//      WHERE wu.open_id IS NOT NULL AND  d.status IN('正式','试用','永续') `
+	sql := `SELECT DISTINCT ur.open_id,wu.user_id FROM wx_user AS wu 
+          INNER JOIN company AS c ON c.company_id = wu.company_id 
+          INNER JOIN company_product AS d ON c.company_id=d.company_id
+		INNER join user_record  as ur on wu.user_id=ur.user_id
+          WHERE ur.open_id != "" AND ur.subscribe=1 and ur.create_platform=1 AND  d.status IN('正式','试用','永续') `
+	//if openIdstr != "" {
+	//	sql += ` AND ur.open_id in (` + openIdstr + `) `
+	//}
+	_, err = orm.NewOrm().Raw(sql).QueryRows(&items)
+	return
+}
+
+func GetOpenIdListByMobile(mobile string) (items []*OpenIdList, err error) {
+	//openIdstr := WxUsersGet()
+	sql := `SELECT DISTINCT ur.open_id,wu.user_id FROM wx_user AS wu 
+          INNER JOIN company AS c ON c.company_id = wu.company_id 
+          INNER join user_record  as ur on wu.user_id=ur.user_id
+          WHERE ur.open_id != "" AND ur.subscribe=1 and ur.create_platform=1 AND wu.mobile=? `
+	//if openIdstr != "" {
+	//	sql += ` AND ur.open_id in (` + openIdstr + `) `
+	//}
+	_, err = orm.NewOrm().Raw(sql, mobile).QueryRows(&items)
+	return
+}
+
+// 获取预约活动的用户的openID
+func GetActivityOpenIdList(activityId int) (items []*OpenIdList, err error) {
+	sql := `SELECT DISTINCT cr.open_id,u.user_id
+			FROM
+			cygx_my_schedule AS m
+			INNER JOIN user_record AS u ON u.bind_account = m.mobile 
+			INNER JOIN cygx_user_record AS cr ON cr.union_id = u.union_id 
+			WHERE m.activity_id = ? AND u.create_platform = 4 `
+	_, err = orm.NewOrm().Raw(sql, activityId).QueryRows(&items)
+	return
+}
+
+// 获取预约活动的用户的openID测试环境
+func GetActivityOpenIdListByDeBug(activityId int) (items []*OpenIdList, err error) {
+	sql := `SELECT
+			u.open_id,
+			u.user_id 
+		FROM
+			cygx_my_schedule AS s
+			INNER JOIN wx_user AS wx ON wx.user_id = s.user_id
+			INNER JOIN user_record AS u ON u.bind_account = wx.mobile
+			INNER JOIN company_product AS p ON p.company_id = wx.company_id 
+		WHERE
+			s.activity_id = ? 
+			AND u.create_platform = 1 
+			AND p.STATUS IN ( '正式', '试用', '永续' ) 
+		GROUP BY
+			u.open_id`
+	_, err = orm.NewOrm().Raw(sql, activityId).QueryRows(&items)
+	return
+}
+
+// 获取预约活动的用户的openID
+func GetActivitySpecialOpenIdList() (items []*OpenIdList, err error) {
+	sql := `SELECT DISTINCT cr.open_id,u.user_id
+			FROM
+			cygx_user_follow_special AS m
+			INNER JOIN user_record AS u ON u.bind_account = m.mobile 
+			INNER JOIN cygx_user_record AS cr ON cr.union_id = u.union_id 
+			WHERE  u.create_platform = 4 `
+	_, err = orm.NewOrm().Raw(sql).QueryRows(&items)
+	return
+}
+
+// 获取预约活动的用户的openID测试环境
+func GetActivitySpecialOpenIdListByDeBug() (items []*OpenIdList, err error) {
+	sql := `SELECT
+			u.open_id,
+			u.user_id 
+		FROM
+			cygx_user_follow_special AS s
+			INNER JOIN wx_user AS wx ON wx.user_id = s.user_id
+			INNER JOIN user_record AS u ON u.bind_account = wx.mobile
+			INNER JOIN company_product AS p ON p.company_id = wx.company_id 
+		WHERE
+			u.create_platform = 1 
+			AND p.STATUS IN ( '正式', '试用', '永续' ) 
+		GROUP BY
+			u.open_id`
+	_, err = orm.NewOrm().Raw(sql).QueryRows(&items)
+	return
+}
+
+// 获取关注作者的用户的openID
+func GetFollowDepartmentOpenIdList(departmentId int) (items []*OpenIdList, err error) {
+	sql := `SELECT cr.open_id,u.user_id
+			FROM
+				cygx_article_department_follow AS f
+				INNER JOIN user_record AS u ON u.bind_account = f.mobile
+				INNER JOIN wx_user AS wx ON wx.user_id = f.user_id
+				INNER JOIN company_product AS p ON p.company_id = wx.company_id 
+				INNER JOIN cygx_user_record AS cr ON cr.union_id = u.union_id 
+			WHERE
+				f.department_id = ?
+				AND u.create_platform = 4 
+				AND f.type = 1 
+				AND p.status IN ('正式','试用','永续')
+			GROUP BY
+				cr.open_id `
+	_, err = orm.NewOrm().Raw(sql, departmentId).QueryRows(&items)
+	return
+}
+
+// 获取关注作者的用户的openID测试环境
+func GetFollowDepartmentOpenIdListByDeBug(departmentId int) (items []*OpenIdList, err error) {
+	sql := `SELECT
+			u.open_id,
+			u.user_id 
+		FROM
+			cygx_article_department_follow AS f
+			INNER JOIN wx_user AS wx ON wx.user_id = f.user_id
+			INNER JOIN user_record AS u ON u.bind_account = wx.mobile
+			INNER JOIN company_product AS p ON p.company_id = wx.company_id
+		WHERE
+			f.department_id = ? 
+			AND u.create_platform = 1
+			AND f.type = 1 
+			AND p.STATUS IN ( '正式', '试用', '永续' ) 
+		GROUP BY
+			u.open_id`
+	_, err = orm.NewOrm().Raw(sql, departmentId).QueryRows(&items)
+	return
+}
+
+// 获取关注产业的用户的openID
+func GetFollowindustrialOpenIdList(industrialManagementId int) (items []*OpenIdList, err error) {
+	sql := `SELECT cr.open_id,u.user_id
+			FROM
+			cygx_industry_fllow AS f
+			INNER JOIN wx_user AS wx ON wx.user_id = f.user_id
+			INNER JOIN user_record AS u ON u.bind_account = wx.mobile 
+			INNER JOIN cygx_user_record AS cr ON cr.union_id = u.union_id 
+			WHERE
+			f.industrial_management_id = ? 
+			AND u.create_platform = 4 
+			AND f.type = 1 GROUP BY cr.open_id `
+	_, err = orm.NewOrm().Raw(sql, industrialManagementId).QueryRows(&items)
+	return
+}
+
+// 获取关注产业的用户的openID 测试环境
+func GetFollowindustrialOpenIdListByDeBug(industrialManagementId int) (items []*OpenIdList, err error) {
+	sql := `SELECT
+			u.open_id,
+			u.user_id 
+		FROM
+			cygx_industry_fllow AS f
+			INNER JOIN wx_user AS wx ON wx.user_id = f.user_id
+			INNER JOIN user_record AS u ON u.bind_account = wx.mobile
+		WHERE
+			f.industrial_management_id = ? 
+			AND u.create_platform = 1 
+			AND u.bind_account != ""
+			AND f.type = 1 
+		GROUP BY
+			u.open_id`
+	_, err = orm.NewOrm().Raw(sql, industrialManagementId).QueryRows(&items)
+	return
+}
+
+// GetUserOpenidListByUserIds 根据用户id字符串集合来获取他的openid列表集合
+func GetUserOpenidListByUserIds(userIdStr []string) (list []*OpenIdList, err error) {
+	if len(userIdStr) <= 0 {
+		return
+	}
+	sql := `SELECT open_id,u.user_id FROM user_record WHERE user_id in (` + strings.Join(userIdStr, ",") + `) and create_platform = 1`
+	_, err = orm.NewOrm().Raw(sql).QueryRows(&list)
+	return
+}
+
+func GetAdminOpenIdByMobile(mobile string) (items []*OpenIdList, err error) {
+	sql := `SELECT DISTINCT ur.open_id,wu.user_id FROM wx_user AS wu 
+          INNER JOIN company AS c ON c.company_id = wu.company_id 
+          INNER join user_record  as ur on wu.user_id=ur.user_id
+          WHERE ur.open_id != "" and ur.create_platform=1 AND wu.mobile=? `
+	_, err = orm.NewOrm().Raw(sql, mobile).QueryRows(&items)
+	return
+}
+
+// 根据手机号获取用户的openid查研观向小助手专用
+func GetUserRecordListByMobile(platform int, bindAccount string) (items []*OpenIdList, err error) {
+	var sql string
+	if utils.RunMode == "release" {
+		sql = `SELECT cr.open_id FROM user_record  as u 
+			INNER JOIN cygx_user_record AS cr ON cr.union_id = u.union_id 
+			WHERE create_platform=? AND bind_account IN (` + bindAccount + `)`
+	} else {
+		platform = 1
+		sql = `SELECT open_id FROM	user_record  WHERE create_platform =? AND bind_account IN (` + bindAccount + `)`
+	}
+	_, err = orm.NewOrm().Raw(sql, platform).QueryRows(&items)
+	return
+}
+
+// 获取单个用户openid
+func GetCompanyDetailByIdGroup(platform int, bindAccount string) (item *OpenIdList, err error) {
+	o := orm.NewOrm()
+	var sql string
+	sql = `SELECT cr.open_id,wu.user_id FROM user_record  as u 
+			INNER JOIN cygx_user_record AS cr ON cr.union_id = u.union_id 
+			INNER JOIN wx_user AS wu ON wu.mobile = u.bind_account 
+			WHERE create_platform=? AND u.bind_account = ?`
+	err = o.Raw(sql, platform, bindAccount).QueryRow(&item)
+	return
+}
+
+// 获取小助手所有的用户的openid
+func GetCygxUserAllOpneid() (items []*OpenIdList, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+			cr.open_id,
+			r.user_id 
+		FROM
+			user_record AS r
+			INNER JOIN cygx_user_record AS cr ON cr.union_id = r.union_id 
+		WHERE
+			r.create_platform = 4`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+func GetOpenIdArr() (items []string, err error) {
+	sql := ` SELECT DISTINCT ur.open_id FROM wx_user AS wu 
+          INNER JOIN company AS c ON c.company_id = wu.company_id 
+          INNER JOIN company_product AS d ON c.company_id=d.company_id
+		INNER JOIN user_record  AS ur ON wu.user_id=ur.user_id
+          WHERE ur.open_id != "" AND ur.subscribe=1 AND ur.create_platform=1 AND  d.status IN('正式','试用','永续')
+         ORDER BY FIELD(c.company_id, 16) desc, ur.user_record_id asc`
+	_, err = orm.NewOrm().Raw(sql).QueryRows(&items)
+	return
+}
+
+// 获取预约活动的用户的openID测试环境
+func GetActivitySpecialOpenIdListMobile(condition string, pars []interface{}) (items []*OpenIdList, err error) {
+	sql := `SELECT
+			cr.open_id,
+			u.user_id
+		FROM
+			company_report_permission AS p
+			INNER JOIN wx_user AS u ON u.company_id = p.company_id
+			INNER JOIN user_record AS r ON r.user_id = u.user_id
+			INNER JOIN cygx_user_record AS cr ON cr.union_id = r.union_id 
+		WHERE
+			 r.create_platform = 4  AND p.STATUS IN ('正式','试用','永续')  ` + condition + ` GROUP BY cr.open_id`
+	_, err = orm.NewOrm().Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// 获取所有关注了该产业用户的openid
+func GetCygxUserIndustryFllowOpneid(IndustrialManagementId int) (items []*OpenIdList, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+			cr.open_id,
+			r.user_id 
+		FROM
+			user_record AS r
+			INNER JOIN cygx_user_record AS cr ON cr.union_id = r.union_id 
+			INNER join cygx_industry_fllow  as cf on cf.user_id = r.user_id 
+		WHERE
+			r.create_platform = 4
+			AND cf.industrial_management_id = ?`
+	_, err = o.Raw(sql, IndustrialManagementId).QueryRows(&items)
+	return
+}
+
+// 获取所有关注了该产业用户的openid
+func GetCygxUserIndustryFllowOpneidByActivityIds(activityId int) (items []*OpenIdList, err error) {
+	o := orm.NewOrm()
+	sql := `			
+SELECT
+	cr.open_id,
+	r.user_id 
+FROM
+	cygx_industrial_activity_group_management AS agm
+	INNER JOIN cygx_industry_fllow AS f ON f.industrial_management_id = agm.industrial_management_id
+	INNER JOIN user_record AS r
+	INNER JOIN cygx_user_record AS cr 
+WHERE
+	agm.activity_id = ? 
+	AND cr.union_id = r.union_id 
+	AND r.create_platform = 4 
+	AND r.user_id = f.user_id;`
+	_, err = o.Raw(sql, activityId).QueryRows(&items)
+	return
+}
+
+// 获取所有关注了该产业永续客户的openid
+func GetCygxForeverUserIndustryFllowOpneidByActivityIds(activityId int) (items []*OpenIdList, err error) {
+	o := orm.NewOrm()
+	sql := `			
+SELECT
+	cr.open_id,
+	r.user_id 
+FROM
+	cygx_industrial_activity_group_management AS agm
+	INNER JOIN cygx_industry_fllow AS f ON f.industrial_management_id = agm.industrial_management_id
+	INNER JOIN user_record AS r
+	INNER JOIN cygx_user_record AS cr 
+	INNER JOIN wx_user AS wx ON wx.user_id = r.user_id 
+	INNER JOIN company_product AS p ON p.company_id = wx.company_id 
+WHERE
+	agm.activity_id = ? 
+	AND cr.union_id = r.union_id 
+	AND r.create_platform = 4 
+	AND r.user_id = f.user_id
+	AND p.status = "永续";`
+	_, err = o.Raw(sql, activityId).QueryRows(&items)
+	return
+}
+
+// 获取所有关注了该产业试用客户的openid
+func GetCygxTryOutUserIndustryFllowOpneidByActivityIds(activityId int) (items []*OpenIdList, err error) {
+	o := orm.NewOrm()
+	sql := `			
+SELECT
+	cr.open_id,
+	r.user_id 
+FROM
+	cygx_industrial_activity_group_management AS agm
+	INNER JOIN cygx_industry_fllow AS f ON f.industrial_management_id = agm.industrial_management_id
+	INNER JOIN user_record AS r
+	INNER JOIN cygx_user_record AS cr 
+	INNER JOIN wx_user AS wx ON wx.user_id = r.user_id 
+	INNER JOIN company_product AS p ON p.company_id = wx.company_id 
+WHERE
+	agm.activity_id = ? 
+	AND cr.union_id = r.union_id 
+	AND r.create_platform = 4 
+	AND r.user_id = f.user_id
+	AND p.status = "试用";`
+	_, err = o.Raw(sql, activityId).QueryRows(&items)
+	return
+}
+
+// 获取所有关注了该产业用户的companyIds
+func GetCygxIndustryFollowCompanyIdsByActivityId(activityId int) (item *string, err error) {
+	o := orm.NewOrm()
+	sql := `			
+SELECT
+	GROUP_CONCAT( DISTINCT f.company_id SEPARATOR ',' ) AS company_ids
+FROM
+	cygx_industrial_activity_group_management AS agm
+	INNER JOIN cygx_industry_fllow AS f ON f.industrial_management_id = agm.industrial_management_id
+	INNER JOIN user_record AS r
+	INNER JOIN cygx_user_record AS cr 
+WHERE
+	agm.activity_id = ? 
+	AND r.create_platform = 4 
+	AND r.user_id = f.user_id;`
+	err = o.Raw(sql, activityId).QueryRow(&item)
+	return
+}
+
+// 获取所有关注了该产业用户的openidBy公司id
+func GetCygxUserIndustryFllowOpneidByActivityIdAndCompanyIds(activityId int, companyIds string) (items []*OpenIdList, err error) {
+	o := orm.NewOrm()
+	sql := `			
+SELECT
+	cr.open_id,
+	r.user_id 
+FROM
+	cygx_industrial_activity_group_management AS agm
+	INNER JOIN cygx_industry_fllow AS f ON f.industrial_management_id = agm.industrial_management_id
+	INNER JOIN user_record AS r
+	INNER JOIN cygx_user_record AS cr 
+WHERE
+	agm.activity_id = ? 
+	AND cr.union_id = r.union_id 
+	AND r.create_platform = 4 
+	AND r.user_id = f.user_id
+	AND f.company_id IN (` + companyIds + `);`
+	_, err = o.Raw(sql, activityId).QueryRows(&items)
+	return
+}
+
+// 获取所有互动过的用户id
+func GetCygxInteractiveUserByActivityId(activityId int) (items []*int, err error) {
+	o := orm.NewOrm()
+	sql := `			
+SELECT user_id FROM cygx_activity_signup WHERE activity_id =? AND fail_type = 0
+UNION ALL
+SELECT user_id FROM cygx_activity_appointment WHERE activity_id = ?
+UNION ALL
+SELECT user_id FROM cygx_activity_meeting_reminder WHERE activity_id = ?
+UNION ALL
+SELECT user_id FROM cygx_activity_help_ask  WHERE activity_id = ? `
+	_, err = o.Raw(sql, activityId, activityId, activityId, activityId).QueryRows(&items)
+	return
+}
+
+// 获取所有用户的openid
+func GetCygxUserOpneidByUserIds(userIds string) (items []*OpenIdList, err error) {
+	o := orm.NewOrm()
+	sql := `			
+SELECT
+	cr.open_id,
+	r.user_id 
+FROM
+	user_record AS r
+	INNER JOIN cygx_user_record AS cr 
+WHERE
+	cr.union_id = r.union_id 
+	AND r.create_platform = 4 
+	AND r.user_id IN (` + userIds + `);`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+func GetOpenIdArrByClassifyNameSecond(classifyNameSecond string) (items []string, err error) {
+	sql := ` SELECT DISTINCT ur.open_id FROM wx_user AS wu 
+			INNER JOIN company AS c ON c.company_id = wu.company_id 
+			INNER JOIN company_product AS d ON c.company_id=d.company_id
+			INNER JOIN user_record  AS ur ON wu.user_id=ur.user_id
+			INNER JOIN company_report_permission AS e ON d.company_id=e.company_id
+			INNER JOIN chart_permission AS f ON e.chart_permission_id=f.chart_permission_id
+			INNER JOIN chart_permission_search_key_word_mapping AS g ON f.chart_permission_id=g.chart_permission_id
+			WHERE ur.open_id != "" AND ur.subscribe=1 AND ur.create_platform=1 AND  d.status IN('正式','试用','永续')
+			AND g.from='rddp'
+			AND g.key_word=?
+			ORDER BY FIELD(c.company_id, 16) DESC, ur.user_record_id ASC  `
+	_, err = orm.NewOrm().Raw(sql, classifyNameSecond).QueryRows(&items)
+	return
+}
+
+func GetOpenIdArrByChartPermissionIds(chartPermissionIds string) (items []string, err error) {
+	sql := ` SELECT DISTINCT ur.open_id FROM wx_user AS wu 
+INNER JOIN company AS c ON c.company_id = wu.company_id 
+INNER JOIN company_product AS d ON c.company_id=d.company_id
+INNER JOIN user_record  AS ur ON wu.user_id=ur.user_id
+INNER JOIN company_report_permission AS e ON d.company_id=e.company_id
+INNER JOIN chart_permission AS f ON e.chart_permission_id=f.chart_permission_id
+WHERE ur.open_id != "" AND ur.subscribe=1 AND ur.create_platform=1 AND  d.status IN('正式','试用','永续')
+AND f.chart_permission_id IN(` + chartPermissionIds + `)
+ORDER BY FIELD(c.company_id, 16) DESC, ur.user_record_id ASC  `
+	_, err = orm.NewOrm().Raw(sql).QueryRows(&items)
+	return
+}
+
+func GetOpenIdArrByVarietyTag(varietyTagId int) (items []string, err error) {
+	sql := ` SELECT DISTINCT ur.open_id FROM wx_user AS wu 
+INNER JOIN company AS c ON c.company_id = wu.company_id 
+INNER JOIN company_product AS d ON c.company_id=d.company_id
+INNER JOIN user_record  AS ur ON wu.user_id=ur.user_id
+INNER JOIN company_report_permission AS e ON d.company_id=e.company_id
+INNER JOIN chart_permission AS f ON e.chart_permission_id=f.chart_permission_id
+INNER JOIN variety_tag AS g ON f.chart_permission_id=g.chart_permission_id
+WHERE ur.open_id != "" AND ur.subscribe=1 AND ur.create_platform=1 AND  d.status IN('正式','试用','永续')
+AND g.variety_tag_id=?
+ORDER BY FIELD(c.company_id, 16) DESC, ur.user_record_id ASC  `
+	_, err = orm.NewOrm().Raw(sql, varietyTagId).QueryRows(&items)
+	return
+}

+ 60 - 0
models/xfyun.go

@@ -0,0 +1,60 @@
+package models
+
+type XfSendParam struct {
+	Common struct {
+		AppId string `json:"app_id"`
+	} `json:"common"`
+	Business struct {
+		Aue    string `json:"aue"`
+		Sfl    int    `json:"sfl"`
+		Auf    string `json:"auf"`
+		Vcn    string `json:"vcn"`
+		Speed  int    `json:"speed"`
+		Volume int    `json:"volume"`
+		Pitch  int    `json:"pitch"`
+		Bgs    int    `json:"bgs"`
+		Tte    string `json:"tte"`
+		Reg    string `json:"reg"`
+		Rdn    string `json:"rdn"`
+	} `json:"business"`
+	Data struct {
+		Text   string `json:"text"`
+		Status int    `json:"status"`
+	} `json:"data"`
+}
+
+type XfReciveResult struct {
+	Code    int
+	Message string
+	Sid     string
+	Data    *struct {
+		Audio  string `json:"audio"`
+		Ced    string `json:"ced"`
+		Status int    `json:"status"`
+	} `json:"data"`
+}
+
+type AudioReq struct {
+	ReportId string
+}
+
+type EdbdataImportReq struct {
+	FullPath  string
+	SysUserId string
+}
+
+type EdbdataExportExcelReq struct {
+	StartDate  string
+	EndDate    string
+	Frequency  string
+	ClassifyId int
+	KeyWord    string
+	Mobile     string
+}
+
+// 内容转音频后的结构体
+type VideoInfo struct {
+	VideoUrl         string  `description:"链接"`
+	VideoPlaySeconds float64 `description:"时长"`
+	VideoSize        string  `description:"大小"`
+}

+ 315 - 0
routers/commentsRouter.go

@@ -907,6 +907,321 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "CheckDayWeekReportChapterVideo",
+            Router: `/CheckDayWeekReportChapterVideo`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "AddDayWeekReport",
+            Router: `/addDayWeekReport`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "Author",
+            Router: `/author`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "ClassifyIdDetail",
+            Router: `/classifyIdDetail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "Delete",
+            Router: `/delete`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "EditChapterTrendTag",
+            Router: `/editChapterTrendTag`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "EditDayWeekChapter",
+            Router: `/editDayWeekChapter`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "EditDayWeekReport",
+            Router: `/editDayWeekReport`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "GetChapterTrendTag",
+            Router: `/getChapterTrendTag`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "GetDayReportTickerList",
+            Router: `/getDayReportTickerList`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "GetDayWeekChapter",
+            Router: `/getDayWeekChapter`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "GetDayWeekReportChapterTypeList",
+            Router: `/getDayWeekReportChapterTypeList`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "GetDayWeekReportPauseTime",
+            Router: `/getDayWeekReportPauseTime`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "GetDayWeekReportVideoList",
+            Router: `/getDayWeekReportVideoList`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "GetLastDayWeekReportChapter",
+            Router: `/getLastDayWeekReportChapter`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "GetReportChapterList",
+            Router: `/getReportChapterList`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "GetStopDayWeekReportChapterTypeList",
+            Router: `/getStopDayWeekReportChapterTypeList`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "IsLastDayWeekReportChapter",
+            Router: `/isLastDayWeekReportChapter`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "ListReport",
+            Router: `/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "MarkEditStatus",
+            Router: `/mark`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "PublishReport",
+            Router: `/publish`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "PublishCancleReport",
+            Router: `/publish/cancle`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "PublishDayWeekReport",
+            Router: `/publishDayWeekReport`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "PublishDayWeekReportChapter",
+            Router: `/publishDayWeekReportChapter`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "SaveReportContent",
+            Router: `/saveReportContent`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "SendMsg",
+            Router: `/sendMsg`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "SendTemplateMsg",
+            Router: `/sendTemplateMsg`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "SetDayWeekReportEnableRule",
+            Router: `/setDayWeekReportEnableUpdateRule`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "SetDayWeekReportUpdateRule",
+            Router: `/setDayWeekReportUpdateRule`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "ThsSendTemplateMsg",
+            Router: `/ths/sendTemplateMsg`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "Upload",
+            Router: `/upload`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportUploadCommonController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ReportUploadCommonController"],
+        beego.ControllerComments{
+            Method: "UploadImg",
+            Router: `/uploadImg`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ResourceController"] = append(beego.GlobalControllerRouter["hongze/hongze_ETA_mobile_api/controllers:ResourceController"],
         beego.ControllerComments{
             Method: "Upload",

+ 77 - 0
services/elastic.go

@@ -0,0 +1,77 @@
+package services
+
+import (
+	"context"
+	"fmt"
+	"github.com/olivere/elastic/v7"
+	"hongze/hongze_ETA_mobile_api/models"
+	"strings"
+)
+
+func NewClient() (client *elastic.Client, err error) {
+	client, err = elastic.NewClient(
+		elastic.SetURL(ES_URL),
+		elastic.SetBasicAuth(ES_USERNAME, ES_PASSWORD),
+		elastic.SetSniff(false))
+	return
+}
+
+// EsAddOrEditReport 新增编辑es报告
+func EsAddOrEditReport(indexName, docId string, item *models.ElasticReportDetail) (err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("EsAddOrEditReport Err:", err.Error())
+		}
+	}()
+	client, err := NewClient()
+	if err != nil {
+		return
+	}
+	// 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{}{
+			"ReportId":           item.ReportId,
+			"ReportChapterId":    item.ReportChapterId,
+			"Title":              item.Title,
+			"Abstract":           item.Abstract,
+			"BodyContent":        item.BodyContent,
+			"PublishTime":        item.PublishTime,
+			"PublishState":       item.PublishState,
+			"Author":             item.Author,
+			"ClassifyIdFirst":    item.ClassifyIdFirst,
+			"ClassifyNameFirst":  item.ClassifyNameFirst,
+			"ClassifyIdSecond":   item.ClassifyIdSecond,
+			"ClassifyNameSecond": item.ClassifyNameSecond,
+			"Categories":         item.Categories,
+			"StageStr":           item.StageStr,
+		}).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
+}

+ 69 - 0
services/elasticsearch.go

@@ -0,0 +1,69 @@
+package services
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"github.com/olivere/elastic/v7"
+)
+
+const (
+	ES_URL      = "http://es-cn-nif227b580019rgw6.public.elasticsearch.aliyuncs.com:9200" //<1>
+	ES_USERNAME = "elastic"                                                               //<2>
+	ES_PASSWORD = "hongze@2021"                                                           //<3>
+	//Grafana pwd-> 20521bb9
+	//Grafana username-> emon
+)
+
+type tracelog struct{}
+
+// 实现输出
+func (tracelog) Printf(format string, v ...interface{}) {
+	fmt.Printf(format, v...)
+}
+
+func RemoveDuplicatesAndEmpty(a []string) (ret []string) {
+	a_len := len(a)
+	for i := 0; i < a_len; i++ {
+		if (i > 0 && a[i-1] == a[i]) || len(a[i]) == 0 {
+			continue
+		}
+		ret = append(ret, a[i])
+	}
+	return
+}
+
+func init21123() {
+	fmt.Println("start")
+	client, err := elastic.NewClient(elastic.SetURL(ES_URL), elastic.SetBasicAuth(ES_USERNAME, ES_PASSWORD), elastic.SetSniff(false))
+	if err != nil {
+		fmt.Println("err:", err)
+	}
+	fmt.Println(client)
+	keyWordStr := "医疗器械"
+	queryString := elastic.NewQueryStringQuery(`Title:(` + keyWordStr + `)`)
+
+	boolqueryJson, err := json.Marshal(queryString)
+	fmt.Println("err:", err)
+	fmt.Println("queryString ", string(boolqueryJson))
+
+	var esIndex = "cygx_article"
+	//boolquery.Must(elastic.NewMatchQuery("Title", keyWord), elastic.NewMatchQuery("BodyText", keyWord))
+	highlight := elastic.NewHighlight()
+	highlight = highlight.Fields(elastic.NewHighlighterField("Title"), elastic.NewHighlighterField("BodyText"))
+	highlight = highlight.PreTags("<font color='red'>").PostTags("</font>")
+	request := client.Search(esIndex).Highlight(highlight).Query(queryString)
+
+	requestJson, err := json.Marshal(request)
+	fmt.Println("err:", err)
+	fmt.Println("requestJson ", string(requestJson))
+
+	searchByMatch, err := request.Do(context.Background())
+
+	if searchByMatch.Hits != nil {
+
+	}
+
+	fmt.Println(searchByMatch)
+	fmt.Println("end")
+}

Fichier diff supprimé car celui-ci est trop grand
+ 534 - 0
services/report.go


+ 192 - 0
services/video.go

@@ -2,11 +2,110 @@ package services
 
 import (
 	"bytes"
+	"encoding/base64"
 	"encoding/binary"
+	"encoding/json"
+	"errors"
+	"github.com/PuerkitoBio/goquery"
+	"github.com/kgiannakakis/mp3duration/src/mp3duration"
+	"hongze/hongze_ETA_mobile_api/models"
+	"hongze/hongze_ETA_mobile_api/services/alarm_msg"
+	"hongze/hongze_ETA_mobile_api/utils"
+	"html"
 	"io"
+	"io/ioutil"
+	"os"
+	"strings"
+	"time"
 	"unicode"
 )
 
+func CreateVideo(report *models.ReportDetail) (err error) {
+	defer func() {
+		if err != nil {
+			utils.FileLog.Error("CreateVideo Err:%s", err.Error())
+			go alarm_msg.SendAlarmMsg("CreateVideo, Err:"+err.Error(), 3)
+			//go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "Err:"+err.Error(), utils.EmailSendToUsers)
+		}
+	}()
+	ct, err := time.Parse(utils.FormatDateTime, report.CreateTime)
+	createTime := ct.Format("0102")
+	videoName := report.Title + "(" + createTime + ")"
+	content := html.UnescapeString(report.Content)
+	content = strings.Replace(content, "Powered", "", -1)
+	content = strings.Replace(content, "by", "", -1)
+	content = strings.Replace(content, "Froala", "", -1)
+	content = strings.Replace(content, "Editor", "", -1)
+	doc, err := goquery.NewDocumentFromReader(strings.NewReader(content))
+	if err != nil {
+		return
+	}
+
+	param := new(models.XfSendParam)
+	param.Common.AppId = utils.XfAPPID
+	param.Business.Aue = "lame"
+	param.Business.Sfl = 1
+	param.Business.Auf = "audio/L16;rate=16000"
+	param.Business.Vcn = utils.XfVcn
+	param.Business.Speed = 50
+	param.Business.Volume = 100
+	param.Business.Pitch = 50
+	param.Business.Bgs = 0
+	param.Business.Tte = "UTF8"
+	param.Business.Reg = "2"
+	param.Business.Rdn = "0"
+	param.Data.Status = 2
+	videoContent := doc.Text()
+
+	saveName := utils.GetRandStringNoSpecialChar(16) + ".mp3"
+	savePath := "./" + saveName
+	//if utils.FileIsExist(savePath) {
+	//	os.Remove(savePath)
+	//}
+	contentArr := GetChineseCount(videoContent)
+	for _, v := range contentArr {
+		newText := v
+		param.Data.Text = base64.StdEncoding.EncodeToString([]byte(newText))
+		result, err := json.Marshal(param)
+		if err != nil {
+			return err
+		}
+		err = GetXfVideo(result, savePath)
+		if err != nil {
+			err = errors.New("GetXfVideo Err:" + err.Error())
+			utils.FileLog.Error("GetXfVideo err", err.Error())
+			return err
+		}
+		time.Sleep(5 * time.Second)
+	}
+	uploadUrl, err := UploadAudioAliyun(saveName, savePath)
+	if err != nil {
+		err = errors.New("UploadAudioAliyun Err:" + err.Error())
+		return
+	}
+
+	fileBody, err := ioutil.ReadFile(savePath)
+	videoSize := len(fileBody)
+	sizeFloat := (float64(videoSize) / float64(1024)) / float64(1024)
+	sizeStr := utils.SubFloatToFloatStr(sizeFloat, 2)
+
+	playSeconds, err := mp3duration.Calculate(savePath)
+	if playSeconds <= 0 {
+		playSeconds, err = utils.GetVideoPlaySeconds(savePath)
+		if err != nil {
+			err = errors.New("GetVideoPlaySeconds Err:" + err.Error())
+			return
+		}
+	}
+
+	if playSeconds > 0 {
+		if utils.FileIsExist(savePath) {
+			os.Remove(savePath)
+		}
+	}
+	err = models.ModifyReportVideo(report.Id, uploadUrl, videoName, sizeStr, playSeconds)
+	return
+}
 
 func GetChineseCount(str1 string) []string {
 	fontArr := make([]string, 0)
@@ -87,3 +186,96 @@ func getFourccType(boxHeader BoxHeader) (fourccType string) {
 	return
 }
 
+// CreateReportVideo 生成报告video
+func CreateReportVideo(reportTitle, reportContent, reportTime string) (uploadUrl, videoName, sizeStr string, playSeconds float64, err error) {
+	defer func() {
+		if err != nil {
+			utils.FileLog.Error("CreateReportVideo Err:%s", err.Error())
+			go alarm_msg.SendAlarmMsg("CreateReportVideo, reportTitle:"+reportTitle+", Err:"+err.Error(), 3)
+			//go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "CreateReportVideo, reportTitle:" + reportTitle +", Err:"+err.Error(), utils.EmailSendToUsers)
+		}
+	}()
+	if reportContent == "" {
+		return
+	}
+	ct, err := time.Parse(utils.FormatDateTime, reportTime)
+	if err != nil {
+		return
+	}
+	createTime := ct.Format("0102")
+	videoName = reportTitle + "(" + createTime + ")"
+	content := html.UnescapeString(reportContent)
+	content = strings.Replace(content, "Powered", "", -1)
+	content = strings.Replace(content, "by", "", -1)
+	content = strings.Replace(content, "Froala", "", -1)
+	content = strings.Replace(content, "Editor", "", -1)
+	doc, err := goquery.NewDocumentFromReader(strings.NewReader(content))
+	if err != nil {
+		return
+	}
+
+	param := new(models.XfSendParam)
+	param.Common.AppId = utils.XfAPPID
+	param.Business.Aue = "lame"
+	param.Business.Sfl = 1
+	param.Business.Auf = "audio/L16;rate=16000"
+	param.Business.Vcn = utils.XfVcn
+	param.Business.Speed = 50
+	param.Business.Volume = 100
+	param.Business.Pitch = 50
+	param.Business.Bgs = 0
+	param.Business.Tte = "UTF8"
+	param.Business.Reg = "2"
+	param.Business.Rdn = "0"
+	param.Data.Status = 2
+	videoContent := doc.Text()
+
+	saveName := utils.GetRandStringNoSpecialChar(16) + ".mp3"
+	savePath := "./" + saveName
+	//if utils.FileIsExist(savePath) {
+	//	os.Remove(savePath)
+	//}
+	contentArr := GetChineseCount(videoContent)
+	for _, v := range contentArr {
+		newText := v
+		param.Data.Text = base64.StdEncoding.EncodeToString([]byte(newText))
+		result, tmpErr := json.Marshal(param)
+		if tmpErr != nil {
+			return
+		}
+		err = GetXfVideo(result, savePath)
+		if err != nil {
+			err = errors.New("GetXfVideo Err:" + err.Error())
+			utils.FileLog.Error("GetXfVideo err", err.Error())
+			return
+		}
+		time.Sleep(5 * time.Second)
+	}
+	uploadUrl, err = UploadAudioAliyun(saveName, savePath)
+	if err != nil {
+		err = errors.New("UploadAudioAliyun Err:" + err.Error())
+		return
+	}
+
+	fileBody, err := ioutil.ReadFile(savePath)
+	videoSize := len(fileBody)
+	sizeFloat := (float64(videoSize) / float64(1024)) / float64(1024)
+	sizeStr = utils.SubFloatToFloatStr(sizeFloat, 2)
+
+	playSeconds, err = mp3duration.Calculate(savePath)
+	if playSeconds <= 0 {
+		playSeconds, err = utils.GetVideoPlaySeconds(savePath)
+		if err != nil {
+			err = errors.New("GetVideoPlaySeconds Err:" + err.Error())
+			return
+		}
+	}
+
+	if playSeconds > 0 {
+		if utils.FileIsExist(savePath) {
+			os.Remove(savePath)
+		}
+	}
+
+	return
+}

+ 222 - 0
services/wechat_send_msg.go

@@ -0,0 +1,222 @@
+package services
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"hongze/hongze_ETA_mobile_api/models"
+	"hongze/hongze_ETA_mobile_api/services/alarm_msg"
+	"hongze/hongze_ETA_mobile_api/utils"
+	"io/ioutil"
+	"net/http"
+	"strconv"
+	"strings"
+)
+
+type SendTemplateResponse struct {
+	Errcode int    `json:"errcode"`
+	Errmsg  string `json:"errmsg"`
+	MsgID   int    `json:"msgid"`
+}
+
+type ClearQuotaResponse struct {
+	Errcode int    `json:"errcode"`
+	Errmsg  string `json:"errmsg"`
+}
+
+// SendMiniProgramReportWxMsg 推送报告微信模板消息-小程序链接
+func SendMiniProgramReportWxMsg(reportId int) (err error) {
+	var msg string
+	reportIdStr := strconv.Itoa(reportId)
+	defer func() {
+		if err != nil {
+			fmt.Println("msg:", msg)
+			utils.FileLog.Error(fmt.Sprintf("SendMiniProgramReportWxMsg, 发送报告模版消息失败, ReportId:%s, Err:%s", reportIdStr, err.Error()))
+			go alarm_msg.SendAlarmMsg("SendMiniProgramReportWxMsg发送报告模版消息失败;"+"ReportId:"+reportIdStr+",Err:"+err.Error()+";msg:"+msg, 3)
+			//go utils.SendEmail("SendMiniProgramReportWxMsg发送报告模版消息失败"+"【"+utils.APPNAME+"】"+"【"+utils.RunMode+"】"+time.Now().Format("2006-01-02 15:04:05"), "ReportId:"+reportIdStr+";"+msg+";Err:"+err.Error(), toUser)
+		}
+	}()
+	utils.FileLog.Info("%s", "services SendMsg")
+
+	report, err := models.GetReportById(reportId)
+	if err != nil {
+		msg = "GetReportInfo Err:" + err.Error()
+		return
+	}
+	if report == nil {
+		utils.FileLog.Info("报告信息不存在")
+		return
+	}
+	//if report.MsgIsSend == 1 {
+	//	return
+	//}
+	//accessToken, err := models.GetWxAccessToken()
+	//if err != nil {
+	//	msg = "GetWxAccessToken Err:" + err.Error()
+	//	return
+	//}
+	//if accessToken == "" {
+	//	msg = "accessToken is empty"
+	//	return
+	//}
+
+	var openIdArr []string
+	if report.ClassifyIdSecond <= 0 {
+		openIdArr, err = models.GetOpenIdArr()
+		if err != nil {
+			msg = "get GetOpenIdArr err:" + err.Error()
+			return
+		}
+	} else {
+		classify, err := models.GetClassifyById(report.ClassifyIdSecond)
+		if err != nil {
+			msg = "获取报告分类失败 err:" + err.Error()
+			return err
+		}
+		if classify.IsMassSend == 1 {
+			openIdArr, err = models.GetOpenIdArr()
+			if err != nil {
+				msg = "get GetOpenIdArr err:" + err.Error()
+				return err
+			}
+		} else {
+			openIdArr, err = models.GetOpenIdArrByClassifyNameSecond(report.ClassifyNameSecond)
+			if err != nil {
+				msg = "GetOpenIdArrByClassifyNameSecond err:" + err.Error()
+				return err
+			}
+		}
+	}
+
+	//sendUrl := "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + accessToken
+	//fmt.Println("send start")
+	//utils.FileLog.Info("send start")
+	//sendMap := make(map[string]interface{})
+	//sendData := make(map[string]interface{})
+
+	title := fmt.Sprintf("弘则%s", report.ClassifyNameFirst)
+	if CheckTwoWeekOrMonthReport(report.ClassifyIdFirst, report.ClassifyNameFirst) {
+		title = fmt.Sprintf("弘则%s", report.ClassifyNameSecond)
+	}
+	//redirectUrl := utils.TemplateRedirectUrl + strconv.Itoa(reportId)
+	first := fmt.Sprintf("Hi,最新一期%s已上线,欢迎查看", report.ClassifyNameFirst)
+	keyword1 := title
+	keyword2 := report.Title
+	keyword3 := report.PublishTime
+	keyword4 := report.Abstract
+
+	//sendData["first"] = map[string]interface{}{"value": first, "color": "#173177"}
+	//sendData["keyword1"] = map[string]interface{}{"value": keyword1, "color": "#173177"}
+	//sendData["keyword2"] = map[string]interface{}{"value": keyword2, "color": "#173177"}
+	//sendData["keyword3"] = map[string]interface{}{"value": keyword3, "color": "#173177"}
+	//sendData["keyword4"] = map[string]interface{}{"value": keyword4, "color": "#173177"}
+	//
+	//sendMap["template_id"] = utils.TemplateIdByProduct
+	////sendMap["url"] = redirectUrl
+	//sendMap["data"] = sendData
+
+	var wxAppPath string
+	if report.ChapterType == utils.REPORT_TYPE_WEEK {
+		wxAppPath = fmt.Sprintf("pages-report/chapterList?reportId=%s", reportIdStr)
+	} else {
+		wxAppPath = fmt.Sprintf("pages-report/reportDetail?reportId=%s", reportIdStr)
+	}
+
+	//if wxAppPath != "" {
+	//	sendMap["miniprogram"] = map[string]interface{}{"appid": utils.WxYbAppId, "pagepath": wxAppPath}
+	//}
+	//err = sendTemplateMsg(sendUrl, sendMap, openIdList, wxAppPath, utils.TEMPLATE_MSG_REPORT)
+
+	sendInfo := new(SendWxTemplate)
+	sendInfo.First = first
+	sendInfo.Keyword1 = keyword1
+	sendInfo.Keyword2 = keyword2
+	sendInfo.Keyword3 = keyword3
+	sendInfo.Keyword4 = keyword4
+	sendInfo.TemplateId = utils.TemplateIdByProduct
+	sendInfo.RedirectUrl = wxAppPath
+	sendInfo.Resource = wxAppPath
+	sendInfo.SendType = utils.TEMPLATE_MSG_REPORT
+	sendInfo.OpenIdArr = openIdArr
+	sendInfo.RedirectTarget = 1
+	err = SendTemplateMsg(sendInfo)
+
+	return
+}
+
+// CheckTwoWeekOrMonthReport 校验推送报告是否为双周报或者月报
+func CheckTwoWeekOrMonthReport(classifyId int, classifyName string) (ok bool) {
+	if utils.RunMode == "debug" {
+		miniStrArr := []string{
+			"双周报", "月报",
+		}
+		if utils.InArrayByStr(miniStrArr, classifyName) {
+			ok = true
+		}
+	} else {
+		// 此处生产环境用ID主要是担心分类改了名字...
+		IdArr := []int{
+			96, 112,
+		}
+		if utils.InArrayByInt(IdArr, classifyId) {
+			ok = true
+		}
+	}
+	return
+}
+
+type SendWxTemplate struct {
+	WxAppId        string   `description:"公众号appId"`
+	First          string   `description:"模板消息first字段"`
+	Keyword1       string   `description:"模板消息keyword1字段"`
+	Keyword2       string   `description:"模板消息keyword2字段"`
+	Keyword3       string   `description:"模板消息keyword3字段"`
+	Keyword4       string   `description:"模板消息keyword4字段"`
+	Keyword5       string   `description:"模板消息keyword5字段"`
+	Remark         string   `description:"模板消息remark字段"`
+	TemplateId     string   `description:"模板id"`
+	RedirectUrl    string   `description:"跳转地址"`
+	RedirectTarget int      `description:"小程序跳转目标:1:弘则研报小程序,2:随手办公小程序"`
+	Resource       string   `description:"资源唯一标识"`
+	SendType       int      `description:"发送的消息类型:1:报告,2:指标更新提醒,3:审批通知,4:销售领取客户通知,5:活动取消通知,6活动更改时间通知,7:关注的作者发布报告通知,8:发送日报(周报、双周报、月报)模板消息,9:活动预约/报名时间通知"`
+	OpenIdArr      []string `description:"消息接收者openid"`
+}
+
+// 推送模板消息
+func SendTemplateMsg(sendInfo *SendWxTemplate) (err error) {
+	postData, err := json.Marshal(sendInfo)
+	if err != nil {
+		alarm_msg.SendAlarmMsg("SendTemplateMsg json.Marshal Err:"+err.Error(), 1)
+		return err
+	}
+	body := ioutil.NopCloser(strings.NewReader(string(postData)))
+	client := &http.Client{}
+	req, err := http.NewRequest("POST", utils.SendWxTemplateMsgUrl, body)
+	if err != nil {
+		alarm_msg.SendAlarmMsg("SendTemplateMsg http.NewRequest Err:"+err.Error(), 1)
+		return err
+	}
+	contentType := "application/json;charset=utf-8"
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.SendTemplateMsgAuthorization)
+	resp, err := client.Do(req)
+	if err != nil {
+		fmt.Println("http client.Do Err:" + err.Error())
+		return err
+	}
+	defer resp.Body.Close()
+	b, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return err
+	}
+	result := new(models.BaseResponse)
+	err = json.Unmarshal(b, &result)
+	if err != nil {
+		return err
+	}
+	if result.Ret != 200 {
+		err = errors.New(string(b))
+		return err
+	}
+	return
+}

+ 108 - 0
services/xfyun.go

@@ -0,0 +1,108 @@
+package services
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/crypt"
+	"hongze/hongze_ETA_mobile_api/models"
+	"net/url"
+	"strings"
+	"time"
+
+	"github.com/gorilla/websocket"
+	"hongze/hongze_ETA_mobile_api/utils"
+)
+
+// 科大讯飞,语音合成
+func GetXfVideo(body []byte, savePath string) (err error) {
+	path, err := assembleAuthUrl()
+	if err != nil {
+		return
+	}
+	conn, _, err := websocket.DefaultDialer.Dial(path, nil)
+	if err != nil {
+		return
+	}
+	defer conn.Close()
+
+	err = conn.WriteMessage(websocket.TextMessage, body)
+	if err != nil {
+		return
+	}
+
+	for {
+		_, message, err := conn.ReadMessage()
+		if err != nil {
+			fmt.Println("ReadMessage Err:" + err.Error())
+			return err
+		}
+		item := new(models.XfReciveResult)
+		err = json.Unmarshal(message, &item)
+		if err != nil {
+			fmt.Println("json.Unmarshal Err:" + err.Error())
+			return err
+		}
+		if item.Code != 0 {
+			goto readWebSocketFail
+		}
+		if item.Code == 0 && item.Data != nil {
+			if item.Data.Status == 1 {
+				audio := item.Data.Audio
+				err = utils.SaveBase64ToFileBySeek(audio, savePath)
+				if err != nil {
+					fmt.Println("文件保存失败", err.Error())
+					goto readWebSocketFail
+				}
+			} else {
+				audio := item.Data.Audio
+				err = utils.SaveBase64ToFileBySeek(audio, savePath)
+				if err != nil {
+					fmt.Println("文件保存失败", err.Error())
+					goto webSocketClose
+					//return
+				}
+				fmt.Println("goto close")
+				goto webSocketClose
+			}
+		}
+	}
+readWebSocketFail:
+	conn.Close()
+	fmt.Println("goto readWebSocketFail")
+webSocketClose:
+	conn.Close()
+	fmt.Println("goto webSocketClose")
+	return nil
+}
+
+// @hosturl :  like  wss://iat-api.xfyun.cn/v2/iat
+// @apikey : apiKey
+// @apiSecret : apiSecret
+func assembleAuthUrl() (callUrl string, err error) {
+	ul, err := url.Parse(utils.XfHostUrl)
+	if err != nil {
+		return
+	}
+	//签名时间
+	date := time.Now().UTC().Format(time.RFC1123)
+	//参与签名的字段 host ,date, request-line
+	signString := []string{"host: " + ul.Host, "date: " + date, "GET " + ul.Path + " HTTP/1.1"}
+	//拼接签名字符串
+	sign := strings.Join(signString, "\n")
+	fmt.Println("sign:", sign)
+	//签名结果
+	sha := crypt.HmacSha256EncryptToBase64([]byte(sign), []byte(utils.XfAPISecret))
+	//构建请求参数 此时不需要urlencoding
+	authUrl := fmt.Sprintf("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", utils.XfAPIKey,
+		"hmac-sha256", "host date request-line", sha)
+	//将请求参数使用base64编码
+	authorization := base64.StdEncoding.EncodeToString([]byte(authUrl))
+	v := url.Values{}
+	v.Add("host", ul.Host)
+	v.Add("date", date)
+	v.Add("authorization", authorization)
+	//将编码后的字符串url encode后添加到url后面
+	callUrl = utils.XfHostUrl + "?" + v.Encode()
+	return
+}

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff