Răsfoiți Sursa

同步报告审批接口

hsun 1 an în urmă
părinte
comite
b2e948fee7

+ 1 - 1
.gitignore

@@ -7,4 +7,4 @@
 /.idea/
 /.DS_Store
 *.tar.gz
-eta_mobile.exe
+*.exe

+ 0 - 150
controllers/business_conf.go

@@ -1,14 +1,9 @@
 package controllers
 
 import (
-	"encoding/json"
 	"eta/eta_mobile/models"
 	"eta/eta_mobile/utils"
 	"fmt"
-	"html"
-	"strconv"
-	"strings"
-	"time"
 )
 
 // BusinessConfController 商家配置
@@ -20,151 +15,6 @@ type BusinessConfOpenController struct {
 	BaseCommonController
 }
 
-// Save
-// @Title 保存配置
-// @Description 保存配置
-// @Param	request	body map[string]interface{} true "type json string"
-// @Success 200 Ret=200 操作成功
-// @router /save [post]
-func (this *BusinessConfController) Save() {
-	br := new(models.BaseResponse).Init()
-	defer func() {
-		if br.ErrMsg == "" {
-			br.IsSendEmail = false
-		}
-		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 map[string]interface{}
-	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
-		br.Msg = "参数解析异常!"
-		br.ErrMsg = "参数解析失败,Err:" + e.Error()
-		return
-	}
-
-	// 获取配置信息
-	confOb := new(models.BusinessConf)
-	list, e := confOb.GetItemsByCondition("", make([]interface{}, 0), []string{}, "")
-	if e != nil {
-		br.Msg = "保存失败"
-		br.ErrMsg = "获取配置列表失败, Err: " + e.Error()
-		return
-	}
-	confMap := make(map[string]*models.BusinessConf)
-	for _, c := range list {
-		confMap[c.ConfKey] = c
-	}
-
-	// 根据配置类型取值
-	updates := make([]models.BusinessConfUpdate, 0)
-	for k, v := range req {
-		// 过滤掉表中没有的key
-		conf := confMap[k]
-		if conf == nil {
-			continue
-		}
-
-		switch conf.ValType {
-		case 1: // 字符串
-			str, ok := v.(string)
-			if !ok {
-				continue
-			}
-			str = strings.TrimSpace(str)
-			if conf.Necessary == 1 && str == "" {
-				br.Msg = conf.Remark + "不可为空"
-				return
-			}
-			updates = append(updates, models.BusinessConfUpdate{
-				ConfKey: k,
-				ConfVal: str,
-			})
-		case 2: // 数值
-			num, ok := v.(float64)
-			if !ok {
-				continue
-			}
-			if conf.Necessary == 1 && num <= 0 {
-				br.Msg = conf.Remark + "不可为空"
-				return
-			}
-			val := strconv.FormatFloat(num, 'f', 0, 64)
-			updates = append(updates, models.BusinessConfUpdate{
-				ConfKey: k,
-				ConfVal: val,
-			})
-		case 3: // 字符串数组
-			arr, ok := v.([]interface{})
-			if !ok {
-				continue
-			}
-			if conf.Necessary == 1 && len(arr) == 0 {
-				br.Msg = conf.Remark + "不可为空"
-				return
-			}
-			strArr := make([]string, 0)
-			for _, a := range arr {
-				if s, ok2 := a.(string); ok2 {
-					strArr = append(strArr, s)
-				}
-			}
-			val := strings.Join(strArr, ",")
-			updates = append(updates, models.BusinessConfUpdate{
-				ConfKey: k,
-				ConfVal: val,
-			})
-		case 4: // 富文本
-			content, ok := v.(string)
-			if !ok {
-				continue
-			}
-			content = strings.TrimSpace(content)
-			if conf.Necessary == 1 && content == "" {
-				br.Msg = conf.Remark + "不可为空"
-				return
-			}
-			content = html.EscapeString(content)
-			updates = append(updates, models.BusinessConfUpdate{
-				ConfKey: k,
-				ConfVal: content,
-			})
-		}
-	}
-
-	if len(updates) > 0 {
-		if e = models.UpdateBusinessConfMulti(updates); e != nil {
-			br.Msg = "保存失败"
-			br.ErrMsg = "保存商家配置失败, Err: " + e.Error()
-			return
-		}
-	}
-
-	// 操作日志
-	go func() {
-		b, e := json.Marshal(req)
-		if e != nil {
-			return
-		}
-		recordOb := new(models.BusinessConfOperationRecord)
-		recordOb.SysUserId = sysUser.AdminId
-		recordOb.SysRealName = sysUser.RealName
-		recordOb.Content = string(b)
-		recordOb.CreateTime = time.Now().Local()
-		_ = recordOb.Create()
-	}()
-
-	br.Ret = 200
-	br.Success = true
-	br.Msg = "操作成功"
-}
-
 // Fetch
 // @Title 获取配置
 // @Description 获取配置

+ 316 - 61
controllers/english_report/report.go

@@ -5,6 +5,8 @@ import (
 	"eta/eta_mobile/controllers"
 	"eta/eta_mobile/models"
 	"eta/eta_mobile/models/company"
+	"eta/eta_mobile/models/report_approve"
+	"eta/eta_mobile/models/system"
 	"eta/eta_mobile/services"
 	"eta/eta_mobile/services/alarm_msg"
 	"eta/eta_mobile/services/data"
@@ -15,6 +17,7 @@ import (
 	"sort"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 )
 
@@ -393,10 +396,11 @@ func (this *EnglishReportController) ListReport() {
 	if keyWord != "" {
 		condition += ` AND (title LIKE '%` + keyWord + `%' OR admin_real_name LIKE '%` + keyWord + `%' ) `
 	}
+
 	if timeType == "" {
 		timeType = "publish_time"
 	}
-	if timeType != "publish_time" && timeType != "modify_time" {
+	if timeType != "publish_time" && timeType != "modify_time" && timeType != "approve_time" {
 		br.Msg = "请选择正确的时间"
 		br.ErrMsg = "请选择正确的时间"
 		return
@@ -427,7 +431,6 @@ func (this *EnglishReportController) ListReport() {
 		condition += ` AND state = ? `
 		pars = append(pars, state)
 	}
-
 	if classifyIdRoot > 0 && classifyIdFirst == 0 && classifyIdSecond == 0 {
 		//查询顶级分类下的所有二级分类ID
 		childClassify, err := models.GetEnglishSecondClassifyList([]int{classifyIdRoot})
@@ -485,55 +488,114 @@ func (this *EnglishReportController) ListReport() {
 		}
 	}
 
-	total, err := models.GetEnglishReportListCount(condition, pars, companyType)
-	if err != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取失败,Err:" + err.Error()
-		return
-	}
-	list, err := models.GetEnglishReportList(condition, pars, companyType, startSize, pageSize)
-	if err != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取失败,Err:" + err.Error()
-		return
-	}
+	var total int
+	var errCount, errList, errOther error
+	var authOk bool
+	list := make([]*models.EnglishReportList, 0)
+	failMap := make(map[int]bool, 0)    // 有群发失败记录的研报
+	adminMap := make(map[int]string, 0) // 编辑中的研究员姓名
 
-	// 获取邮件配置-是否有权限群发
-	conf := new(models.EnglishReportEmailConf)
-	authKey := "english_report_email_conf"
-	confAuth, e := company.GetConfigDetailByCode(authKey)
-	if e != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取群发邮件权限失败, Err: " + e.Error()
-		return
-	}
-	if confAuth.ConfigValue == "" {
+	wg := sync.WaitGroup{}
+	wg.Add(3)
+
+	// 列表总数
+	go func() {
+		defer func() {
+			wg.Done()
+		}()
+
+		t, e := models.GetEnglishReportListCount(condition, pars, companyType)
+		if e != nil {
+			errCount = fmt.Errorf("获取英文研报Count失败, Err: %s", e.Error())
+			return
+		}
+		total = t
+	}()
+
+	// 列表数据
+	go func() {
+		defer func() {
+			wg.Done()
+		}()
+
+		// 限制一下富文本字段, 列表用不到
+		fieldArr := []string{
+			"id", "add_type", "classify_id_first", "classify_name_first", "classify_id_second", "classify_name_second", "title", "abstract", "author",
+			"frequency", "create_time", "modify_time", "state", "publish_time", "pre_publish_time", "stage", "msg_is_send", "report_code", "pv", "share_url",
+			"pv_email", "email_state", "from_report_id", "key_takeaways", "admin_id", "admin_real_name", "approve_time",
+		}
+		items, e := models.GetEnglishReportList(condition, pars, companyType, startSize, pageSize, fieldArr)
+		if e != nil {
+			errList = fmt.Errorf("获取英文研报列表失败, Err: %s", e.Error())
+			return
+		}
+		list = items
+	}()
+
+	// 群发权限/失败记录
+	go func() {
+		defer func() {
+			wg.Done()
+		}()
+
+		// 获取邮件配置-是否有权限群发
+		conf := new(models.EnglishReportEmailConf)
+		authKey := "english_report_email_conf"
+		confAuth, e := company.GetConfigDetailByCode(authKey)
+		if e != nil {
+			errOther = fmt.Errorf("获取群发邮件权限失败, Err: %s", e.Error())
+			return
+		}
+		if confAuth.ConfigValue == "" {
+			errOther = fmt.Errorf("群发邮件配置为空")
+			return
+		}
+		if e := json.Unmarshal([]byte(confAuth.ConfigValue), &conf); e != nil {
+			errOther = fmt.Errorf("群发邮件配置有误")
+			return
+		}
+		authArr := strings.Split(conf.SendAuthGroup, ",")
+		if utils.InArrayByStr(authArr, sysUser.RoleTypeCode) {
+			authOk = true
+		}
+
+		// 是否有群发邮件失败的记录,标记红点
+		failList, e := models.GetEnglishReportEmailLogFailList(0)
+		if e != nil {
+			errOther = fmt.Errorf("获取群发邮件记录失败, Err: %s", e.Error())
+			return
+		}
+		for i := range failList {
+			failMap[failList[i].ReportId] = true
+		}
+
+		// 获取admin, 用于匹配编辑中的研究员姓名
+		admins, e := system.GetSysAdminList("", make([]interface{}, 0), []string{"admin_id", "real_name"}, "")
+		if e != nil {
+			errOther = fmt.Errorf("获取系统用户列表失败, Err: %s", e.Error())
+			return
+		}
+		for _, a := range admins {
+			adminMap[a.AdminId] = a.RealName
+		}
+	}()
+	wg.Wait()
+
+	if errCount != nil {
 		br.Msg = "获取失败"
-		br.ErrMsg = "群发邮件配置为空"
+		br.ErrMsg = errCount.Error()
 		return
 	}
-	if e := json.Unmarshal([]byte(confAuth.ConfigValue), &conf); e != nil {
+	if errList != nil {
 		br.Msg = "获取失败"
-		br.ErrMsg = "群发邮件配置有误"
+		br.ErrMsg = errList.Error()
 		return
 	}
-	authOk := false
-	authArr := strings.Split(conf.SendAuthGroup, ",")
-	if utils.InArrayByStr(authArr, sysUser.RoleTypeCode) {
-		authOk = true
-	}
-
-	// 是否有群发邮件失败的记录,标记红点
-	failList, e := models.GetEnglishReportEmailLogFailList(0)
-	if e != nil {
+	if errOther != nil {
 		br.Msg = "获取失败"
-		br.ErrMsg = "获取群发邮件记录失败, Err: " + e.Error()
+		br.ErrMsg = errOther.Error()
 		return
 	}
-	failMap := make(map[int]bool, 0)
-	for i := range failList {
-		failMap[failList[i].ReportId] = true
-	}
 	// 查询分类信息
 	var classifyIdSecondSlice []int
 	for _, item := range list {
@@ -557,36 +619,41 @@ func (this *EnglishReportController) ListReport() {
 		}
 		item.EmailAuth = authOk
 		item.EmailHasFail = failMap[item.Id]
+
 		// 邮箱PV大于0的时候, 不展示最初版本的PV
 		if item.PvEmail > 0 {
 			item.Pv = 0
 		}
 
-		/*key := fmt.Sprint(`crm:enReport:edit:`, item.Id)
-		opUserId, _ := utils.Rc.RedisInt(key)
-		//如果当前没有人操作,获取当前操作人是本人,那么编辑按钮可用
-		if opUserId <= 0 || (opUserId == this.SysUser.AdminId) {
-			item.CanEdit = true
-		} else {
-			adminInfo, errAdmin := system.GetSysUserById(opUserId)
-			if errAdmin != nil {
-				br.Msg = "获取失败"
-				br.ErrMsg = "获取失败,Err:" + errAdmin.Error()
-				return
+		// 报告是否正在编辑中
+		var opUser models.MarkReportItem
+		key := fmt.Sprint(`crm:enReport:edit:`, item.Id)
+		opUserId, e := utils.Rc.RedisInt(key)
+		if e != nil {
+			str, te := utils.Rc.RedisString(key)
+			if te == nil {
+				te = json.Unmarshal([]byte(str), &opUser)
+				if te == nil {
+					opUserId = opUser.AdminId
+				}
 			}
-			item.Editor = adminInfo.RealName
-		}*/
-		markStatus, err := services.UpdateEnReportEditMark(item.Id, this.SysUser.AdminId, 2, this.SysUser.RealName)
-		if err != nil {
-			br.Msg = "查询标记状态失败"
-			br.ErrMsg = "查询标记状态失败,Err:" + err.Error()
-			return
 		}
-		if markStatus.Status == 0 {
+		var ret models.MarkReportResp
+		if opUserId > 0 && opUserId != sysUser.AdminId {
+			editor := opUser.Editor
+			if editor == "" {
+				editor = adminMap[opUserId]
+			}
+			ret.Status = 1
+			ret.Msg = fmt.Sprintf("当前%s正在编辑报告", editor)
+			ret.Editor = editor
+		}
+		if ret.Status == 0 {
 			item.CanEdit = true
 		} else {
-			item.Editor = markStatus.Editor
+			item.Editor = ret.Editor
 		}
+
 		//处理分类名
 		if n, ok := classifyNameMap[item.ClassifyIdSecond]; ok {
 			if n.RootId == 0 {
@@ -598,6 +665,7 @@ func (this *EnglishReportController) ListReport() {
 			item.ClassifyNameRoot = n.RootName
 		}
 	}
+
 	page := paging.GetPaging(currentIndex, pageSize, total)
 	resp := new(models.EnglishReportListResp)
 	resp.Paging = page
@@ -1436,3 +1504,190 @@ func (this *EnglishReportController) EditPolicy() {
 	br.Msg = "保存成功"
 	br.Data = resp
 }
+
+// SubmitApprove
+// @Title 提交审批
+// @Description 提交审批接口
+// @Param	request	body models.ReportSubmitApproveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /approve/submit [post]
+func (this *EnglishReportController) SubmitApprove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	var req models.ReportSubmitApproveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportId: %d", req.ReportId)
+		return
+	}
+
+	reportOb := new(models.EnglishReport)
+	reportItem, e := reportOb.GetItemById(reportId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验当前审批配置, 返回下一个状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateSubmitApprove)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	// 下一个状态不为待审批时, 仅更新状态
+	if state != models.ReportStateWaitApprove {
+		reportItem.State = state
+		e = reportItem.UpdateReport([]string{"State"})
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+
+	// 提交审批
+	approveId, e := services.SubmitReportApprove(report_approve.FlowReportTypeEnglish, reportItem.Id, reportItem.Title, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "提交审批失败, Err: " + e.Error()
+		return
+	}
+	reportItem.ApproveId = approveId
+	reportItem.State = models.ReportStateWaitApprove
+	reportItem.ModifyTime = time.Now().Local()
+	e = reportItem.UpdateReport([]string{"ApproveId", "State", "ModifyTime"})
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// CancelApprove
+// @Title 撤销审批
+// @Description 撤销审批
+// @Param	request	body models.ReportCancelApproveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /approve/cancel [post]
+func (this *EnglishReportController) CancelApprove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	var req models.ReportCancelApproveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportId: %d", req.ReportId)
+		return
+	}
+
+	reportOb := new(models.EnglishReport)
+	reportItem, e := reportOb.GetItemById(reportId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验当前审批配置, 返回下一个状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateCancelApprove)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	// 下一个状态不为待提交时, 仅更新状态
+	if state != models.ReportStateWaitSubmit {
+		reportItem.State = state
+		e = reportItem.UpdateReport([]string{"State"})
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+	if reportItem.ApproveId <= 0 {
+		br.Msg = "报告审批不存在"
+		br.ErrMsg = fmt.Sprintf("报告审批不存在, ApproveId: %d", reportItem.ApproveId)
+		return
+	}
+
+	// 撤销审批
+	e = services.CancelReportApprove(report_approve.FlowReportTypeEnglish, reportItem.Id, reportItem.ApproveId, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "撤销审批失败, Err: " + e.Error()
+		return
+	}
+	//reportItem.ApproveId = 0
+	//reportItem.State = models.ReportStateWaitSubmit
+	//reportItem.ModifyTime = time.Now().Local()
+	//e = reportItem.UpdateReport([]string{"ApproveId", "State", "ModifyTime"})
+	//if e != nil {
+	//	br.Msg = "操作失败"
+	//	br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+	//	return
+	//}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 190 - 2
controllers/report.go

@@ -3,6 +3,7 @@ package controllers
 import (
 	"encoding/json"
 	"eta/eta_mobile/models"
+	"eta/eta_mobile/models/report_approve"
 	"eta/eta_mobile/services"
 	"eta/eta_mobile/services/alarm_msg"
 	"eta/eta_mobile/services/data"
@@ -76,7 +77,7 @@ func (this *ReportController) ListReport() {
 	if timeType == "" {
 		timeType = "publish_time"
 	}
-	if timeType != "publish_time" && timeType != "modify_time" {
+	if timeType != "publish_time" && timeType != "modify_time" && timeType != "approve_time" {
 		br.Msg = "请选择正确的时间"
 		br.ErrMsg = "请选择正确的时间"
 		return
@@ -2193,7 +2194,7 @@ func (this *ReportController) PublishDayWeekReport() {
 		br.IsSendEmail = false
 		return
 	}
-	
+
 	tips, err := services.PublishDayWeekReport(reportId)
 	if err != nil {
 		br.Msg = "发布失败"
@@ -2626,3 +2627,190 @@ func (this *ReportController) PrePublishReport() {
 	br.Success = true
 	br.Msg = "定时发布成功"
 }
+
+// SubmitApprove
+// @Title 提交审批
+// @Description 提交审批接口
+// @Param	request	body models.ReportSubmitApproveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /approve/submit [post]
+func (this *ReportController) SubmitApprove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	var req models.ReportSubmitApproveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportId: %d", req.ReportId)
+		return
+	}
+
+	reportOb := new(models.Report)
+	reportItem, e := reportOb.GetItemById(reportId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验当前审批配置, 返回下一个状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeChinese, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateSubmitApprove)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	// 下一个状态不为待审批时, 仅更新状态
+	if state != models.ReportStateWaitApprove {
+		reportItem.State = state
+		e = reportItem.UpdateReport([]string{"State"})
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+
+	// 提交审批
+	approveId, e := services.SubmitReportApprove(report_approve.FlowReportTypeChinese, reportItem.Id, reportItem.Title, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "提交审批失败, Err: " + e.Error()
+		return
+	}
+	reportItem.ApproveId = approveId
+	reportItem.State = models.ReportStateWaitApprove
+	reportItem.ModifyTime = time.Now().Local()
+	e = reportItem.UpdateReport([]string{"ApproveId", "State", "ModifyTime"})
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// CancelApprove
+// @Title 撤销审批
+// @Description 撤销审批
+// @Param	request	body models.ReportCancelApproveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /approve/cancel [post]
+func (this *ReportController) CancelApprove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	var req models.ReportCancelApproveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportId: %d", req.ReportId)
+		return
+	}
+
+	reportOb := new(models.Report)
+	reportItem, e := reportOb.GetItemById(reportId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验当前审批配置, 返回下一个状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeChinese, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateCancelApprove)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	// 下一个状态不为待提交时, 仅更新状态
+	if state != models.ReportStateWaitSubmit {
+		reportItem.State = state
+		e = reportItem.UpdateReport([]string{"State"})
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+	if reportItem.ApproveId <= 0 {
+		br.Msg = "报告审批不存在"
+		br.ErrMsg = fmt.Sprintf("报告审批不存在, ApproveId: %d", reportItem.ApproveId)
+		return
+	}
+
+	// 撤销审批
+	e = services.CancelReportApprove(report_approve.FlowReportTypeChinese, reportItem.Id, reportItem.ApproveId, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "撤销审批失败, Err: " + e.Error()
+		return
+	}
+	//reportItem.ApproveId = 0
+	//reportItem.State = models.ReportStateWaitSubmit
+	//reportItem.ModifyTime = time.Now().Local()
+	//e = reportItem.UpdateReport([]string{"ApproveId", "State", "ModifyTime"})
+	//if e != nil {
+	//	br.Msg = "操作失败"
+	//	br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+	//	return
+	//}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 57 - 0
controllers/report_approve/report_approve.go

@@ -0,0 +1,57 @@
+package report_approve
+
+import (
+	"encoding/json"
+	"eta/eta_mobile/controllers"
+	"eta/eta_mobile/models"
+	"eta/eta_mobile/models/report_approve"
+	"eta/eta_mobile/services"
+)
+
+// ReportApproveController 报告审批
+type ReportApproveController struct {
+	controllers.BaseAuthController
+}
+
+// CheckApproveOpen
+// @Title 校验分类是否开启审批
+// @Description 校验分类是否开启审批
+// @Param	request	body report_approve.ReportApproveCheckApproveOpenReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /classify/check_open [post]
+func (this *ReportApproveController) CheckApproveOpen() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		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 report_approve.ReportApproveCheckApproveOpenReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验是否开启了审批流
+	opening, e := services.CheckReportOpenApprove(req.ReportType, req.ClassifyFirstId, req.ClassifySecondId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告是否开启审批流失败, Err: " + e.Error()
+		return
+	}
+
+	br.Data = opening
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

BIN
eta_mobile.exe


+ 20 - 6
models/business_conf.go

@@ -9,12 +9,19 @@ import (
 )
 
 const (
-	BusinessConfUseXf          = "UseXf"
-	BusinessConfXfAppid        = "XfAppid"
-	BusinessConfXfApiKey       = "XfApiKey"
-	BusinessConfXfApiSecret    = "XfApiSecret"
-	BusinessConfXfVcn          = "XfVcn"
-	BusinessConfEnPptCoverImgs = "EnPptCoverImgs"
+	BusinessConfUseXf             = "UseXf"
+	BusinessConfXfAppid           = "XfAppid"
+	BusinessConfXfApiKey          = "XfApiKey"
+	BusinessConfXfApiSecret       = "XfApiSecret"
+	BusinessConfXfVcn             = "XfVcn"
+	BusinessConfEnPptCoverImgs    = "EnPptCoverImgs"
+	BusinessConfIsReportApprove   = "IsReportApprove"
+	BusinessConfReportApproveType = "ReportApproveType"
+)
+
+const (
+	BusinessConfReportApproveTypeEta   = "eta"
+	BusinessConfReportApproveTypeOther = "other"
 )
 
 // BusinessConf 商户配置表
@@ -165,3 +172,10 @@ func UpdateBusinessConfMulti(items []BusinessConfUpdate) (err error) {
 	}
 	return
 }
+
+func GetBusinessConfByKey(key string) (item *BusinessConf, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM business_conf WHERE conf_key = ? LIMIT 1`)
+	err = o.Raw(sql, key).QueryRow(&item)
+	return
+}

+ 15 - 0
models/db.go

@@ -4,6 +4,7 @@ import (
 	"eta/eta_mobile/models/data_manage"
 	"eta/eta_mobile/models/data_stat"
 	"eta/eta_mobile/models/ppt_english"
+	"eta/eta_mobile/models/report_approve"
 	"eta/eta_mobile/models/sandbox"
 	saModel "eta/eta_mobile/models/semantic_analysis"
 	"eta/eta_mobile/models/system"
@@ -112,6 +113,9 @@ func init() {
 
 	// initDataStat 数据源统计管理相关表
 	initDataStat()
+
+	// 报告审批
+	initReportApprove()
 }
 
 // initSystem 系统表 数据表
@@ -261,3 +265,14 @@ func initDataStat() {
 		new(data_stat.EdbInfoUpdateLog), // 指标更新/刷新日志列表
 	)
 }
+
+// initReportApprove 报告审批相关表
+func initReportApprove() {
+	orm.RegisterModel(
+		new(report_approve.ReportApprove),        // 审批表
+		new(report_approve.ReportApproveFlow),    // 审批流表
+		new(report_approve.ReportApproveNode),    // 审批节点表
+		new(report_approve.ReportApproveRecord),  // 审批记录表
+		new(report_approve.ReportApproveMessage), // 审批消息表
+	)
+}

+ 35 - 4
models/english_report.go

@@ -3,6 +3,7 @@ package models
 import (
 	"errors"
 	"eta/eta_mobile/utils"
+	"fmt"
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"strings"
@@ -41,6 +42,8 @@ type EnglishReport struct {
 	FromReportId       int       `description:"继承的报告ID(英文策略报告ID)"`
 	AdminId            int       `description:"创建者账号"`
 	AdminRealName      string    `description:"创建者姓名"`
+	ApproveTime        time.Time `description:"审批时间"`
+	ApproveId          int       `description:"审批ID"`
 }
 
 func GetEnglishReportStage(classifyIdFirst, classifyIdSecond int) (count int, err error) {
@@ -291,7 +294,7 @@ func GetEnglishReportListCount(condition string, pars []interface{}, companyType
 	return
 }
 
-func GetEnglishReportList(condition string, pars []interface{}, companyType string, startSize, pageSize int) (items []*EnglishReportList, err error) {
+func GetEnglishReportList(condition string, pars []interface{}, companyType string, startSize, pageSize int, fieldArr []string) (items []*EnglishReportList, err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	//产品权限
 	companyTypeSqlStr := ``
@@ -301,12 +304,17 @@ func GetEnglishReportList(condition string, pars []interface{}, companyType stri
 		companyTypeSqlStr = " AND classify_id_first = 40 "
 	}
 
-	sql := `SELECT * 
-        FROM english_report WHERE 1=1  ` + companyTypeSqlStr
+	fields := "*"
+	if len(fieldArr) > 0 {
+		fields = strings.Join(fieldArr, ",")
+	}
+
+	sql := fmt.Sprintf(`SELECT %s FROM english_report WHERE 1=1 %s `, fields, companyTypeSqlStr)
 	if condition != "" {
 		sql += condition
 	}
-	sql += `ORDER BY state ASC, modify_time DESC LIMIT ?,?`
+	// 排序:1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过
+	sql += `ORDER BY FIELD(state,3,1,4,5,6,2), modify_time DESC LIMIT ?,?`
 	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
 	return
 }
@@ -773,3 +781,26 @@ where a.id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `)`
 	_, err = o.Raw(sql, classifyIds).QueryRows(&list)
 	return
 }
+
+func (m *EnglishReport) GetItemById(id int) (item *EnglishReport, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM english_report WHERE id = ? LIMIT 1`
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+// GetEnglishClassifies 获取所有英文分类
+func GetEnglishClassifies() (list []*EnglishClassify, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM english_classify `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// GetEnglishReportStateCount 获取指定状态的报告数量
+func GetEnglishReportStateCount(state int) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT COUNT(1) AS count FROM english_report WHERE state = ?`
+	err = o.Raw(sql, state).QueryRow(&count)
+	return
+}

+ 50 - 1
models/report.go

@@ -7,6 +7,26 @@ import (
 	"time"
 )
 
+// 报告状态
+const (
+	ReportStateUnpublished = 1 // 未发布
+	ReportStatePublished   = 2 // 已发布
+	ReportStateWaitSubmit  = 3 // 待提交
+	ReportStateWaitApprove = 4 // 审批中
+	ReportStateRefused     = 5 // 已驳回
+	ReportStatePass        = 6 // 已通过
+)
+
+// 报告操作
+const (
+	ReportOperateAdd           = 1 // 新增报告
+	ReportOperateEdit          = 2 // 编辑报告
+	ReportOperatePublish       = 3 // 发布报告
+	ReportOperateCancelPublish = 4 // 取消发布报告
+	ReportOperateSubmitApprove = 5 // 提交审批
+	ReportOperateCancelApprove = 6 // 撤回审批
+)
+
 type Report struct {
 	Id                 int       `orm:"column(id)" description:"报告Id"`
 	AddType            int       `description:"新增方式:1:新增报告,2:继承报告"`
@@ -39,6 +59,8 @@ type Report struct {
 	MsgSendTime        time.Time `description:"模版消息发送时间"`
 	AdminId            int       `description:"创建者账号"`
 	AdminRealName      string    `description:"创建者姓名"`
+	ApproveTime        time.Time `description:"审批时间"`
+	ApproveId          int       `description:"审批ID"`
 }
 
 type ReportList struct {
@@ -79,6 +101,7 @@ type ReportList struct {
 	Editor             string                    `description:"编辑人"`
 	AdminId            int                       `description:"创建者账号"`
 	AdminRealName      string                    `description:"创建者姓名"`
+	ApproveTime        string                    `description:"审批时间"`
 }
 
 type ReportListResp struct {
@@ -121,7 +144,8 @@ func GetReportList(condition string, pars []interface{}, companyType string, sta
 	if condition != "" {
 		sql += condition
 	}
-	sql += `ORDER BY FIELD(state,3,1,2,4), modify_time DESC LIMIT ?,?`
+	// 排序:1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过
+	sql += `ORDER BY FIELD(state,3,1,4,5,6,2), modify_time DESC LIMIT ?,?`
 	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
 	return
 }
@@ -783,3 +807,28 @@ func SetPrePublishReportById(reportId int, prePublishTime string, preMsgSend int
 	_, err = o.Raw(sql, prePublishTime, preMsgSend, reportId).Exec()
 	return
 }
+
+// ReportSubmitApproveReq 提交审批请求体
+type ReportSubmitApproveReq struct {
+	ReportId int `description:"报告ID"`
+}
+
+// ReportCancelApproveReq 撤回审批请求体
+type ReportCancelApproveReq struct {
+	ReportId int `description:"报告ID"`
+}
+
+func (m *Report) GetItemById(id int) (item *Report, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report WHERE id = ? LIMIT 1`
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+// GetReportStateCount 获取指定状态的报告数量
+func GetReportStateCount(state int) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT COUNT(1) AS count FROM report WHERE state = ?`
+	err = o.Raw(sql, state).QueryRow(&count)
+	return
+}

+ 35 - 0
models/report_approve/constant.go

@@ -0,0 +1,35 @@
+package report_approve
+
+// 报告类型
+const (
+	FlowReportTypeChinese = 1 // 中文研报
+	FlowReportTypeEnglish = 2 // 英文研报
+	FlowReportTypeSmart   = 3 // 智能研报
+)
+
+var FlowReportTypeMap = map[int]string{
+	FlowReportTypeChinese: "中文研报",
+	FlowReportTypeEnglish: "英文研报",
+	FlowReportTypeSmart:   "智能研报",
+}
+
+// 节点审批方式
+const (
+	NodeApproveTypeRoll = 1 // 依次审批
+	NodeApproveTypeAll  = 2 // 会签
+	NodeApproveTypeAny  = 3 // 或签
+)
+
+// 节点审批人类型
+const (
+	NodeUserTypeNormal = "user" // 用户
+	NodeUserTypeRole   = "role" // 角色
+)
+
+// 报告审批状态
+const (
+	ReportApproveStateApproving = 1 // 待审批
+	ReportApproveStatePass      = 2 // 已审批
+	ReportApproveStateRefuse    = 3 // 已驳回
+	ReportApproveStateCancel    = 4 // 已撤销
+)

+ 448 - 0
models/report_approve/report_approve.go

@@ -0,0 +1,448 @@
+package report_approve
+
+import (
+	"eta/eta_mobile/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+// ReportApprove 报告审批表
+type ReportApprove struct {
+	ReportApproveId  int       `orm:"column(report_approve_id);pk" description:"审批ID"`
+	ReportType       int       `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ReportId         int       `description:"报告ID"`
+	ReportTitle      string    `description:"报告标题"`
+	ClassifyFirstId  int       `description:"一级分类ID"`
+	ClassifySecondId int       `description:"二级分类ID"`
+	State            int       `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	FlowId           int       `description:"审批流ID"`
+	FlowVersion      int       `description:"审批流版本"`
+	StartNodeId      int       `description:"开始节点ID"`
+	CurrNodeId       int       `description:"当前节点ID"`
+	ApplyUserId      int       `description:"申请人ID"`
+	ApplyUserName    string    `description:"申请人姓名"`
+	ApproveRemark    string    `description:"审批备注"`
+	ApproveTime      time.Time `description:"审批时间"`
+	CreateTime       time.Time `description:"创建时间"`
+	ModifyTime       time.Time `description:"修改时间"`
+}
+
+var ReportApproveCols = struct {
+	ReportApproveId  string
+	ReportType       string
+	ReportId         string
+	ReportTitle      string `description:"报告标题"`
+	ClassifyFirstId  string `description:"一级分类ID"`
+	ClassifySecondId string `description:"二级分类ID"`
+	State            string
+	FlowId           string
+	FlowVersion      string
+	StartNodeId      string
+	CurrNodeId       string
+	ApplyUserId      string `description:"申请人ID"`
+	ApplyUserName    string `description:"申请人姓名"`
+	ApproveRemark    string `description:"审批备注"`
+	ApproveTime      string `description:"审批时间"`
+	CreateTime       string
+	ModifyTime       string
+}{
+	ReportApproveId:  "report_approve_id",
+	ReportType:       "report_type",
+	ReportId:         "report_id",
+	ReportTitle:      "report_title",
+	ClassifyFirstId:  "classify_first_id",
+	ClassifySecondId: "classify_second_id",
+	State:            "state",
+	FlowId:           "flow_id",
+	FlowVersion:      "flow_version",
+	StartNodeId:      "start_node_id",
+	CurrNodeId:       "curr_node_id",
+	ApplyUserId:      "apply_user_id",
+	ApplyUserName:    "apply_user_name",
+	ApproveRemark:    "approve_remark",
+	ApproveTime:      "approve_time",
+	CreateTime:       "create_time",
+	ModifyTime:       "modify_time",
+}
+
+func (m *ReportApprove) TableName() string {
+	return "report_approve"
+}
+
+func (m *ReportApprove) PrimaryId() string {
+	return ReportApproveCols.ReportApproveId
+}
+
+func (m *ReportApprove) Create() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.ReportApproveId = int(id)
+	return
+}
+
+func (m *ReportApprove) CreateMulti(items []*ReportApprove) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *ReportApprove) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *ReportApprove) Del() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.ReportApproveId).Exec()
+	return
+}
+
+func (m *ReportApprove) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	_, err = o.Raw(sql, menuIds).Exec()
+	return
+}
+
+func (m *ReportApprove) GetItemById(id int) (item *ReportApprove, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *ReportApprove) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *ReportApprove, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *ReportApprove) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *ReportApprove) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*ReportApprove, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *ReportApprove) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*ReportApprove, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// ReportApproveItem 报告审批信息
+type ReportApproveItem struct {
+	ReportApproveId       int    `description:"审批ID"`
+	ReportApproveRecordId int    `description:"审批记录ID"`
+	ReportType            int    `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ReportId              int    `description:"报告ID"`
+	ReportTitle           string `description:"报告标题"`
+	ReportClassify        string `description:"报告分类"`
+	ClassifyFirstId       int    `description:"一级分类ID"`
+	ClassifySecondId      int    `description:"二级分类ID"`
+	State                 int    `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	RecordState           int    `description:"审批记录状态:1-待审批;2-已通过;3-已驳回"`
+	FlowId                int    `description:"审批流ID"`
+	FlowVersion           int    `description:"审批流版本"`
+	StartNodeId           int    `description:"开始节点ID"`
+	CurrNodeId            int    `description:"当前节点ID"`
+	ApplyUserId           int    `description:"申请人ID"`
+	ApplyUserName         string `description:"申请人姓名"`
+	ApproveRemark         string `description:"审批备注"`
+	ApproveTime           string `description:"审批时间"`
+	HandleTime            string `description:"处理时间"`
+	CreateTime            string `description:"创建时间"`
+	ModifyTime            string `description:"修改时间"`
+}
+
+// FormatReportApproveOrm2Item 格式化报告审批
+func FormatReportApproveOrm2Item(origin *ReportApproveItemOrm) (item *ReportApproveItem) {
+	item = new(ReportApproveItem)
+	if origin == nil {
+		return
+	}
+	item.ReportApproveId = origin.ReportApproveId
+	item.ReportApproveRecordId = origin.ReportApproveRecordId
+	item.ReportType = origin.ReportType
+	item.ReportId = origin.ReportId
+	item.ReportTitle = origin.ReportTitle
+	item.ClassifyFirstId = origin.ClassifyFirstId
+	item.ClassifySecondId = origin.ClassifySecondId
+	item.State = origin.State
+	item.RecordState = origin.RecordState
+	item.FlowId = origin.FlowId
+	item.FlowVersion = origin.FlowVersion
+	item.StartNodeId = origin.StartNodeId
+	item.CurrNodeId = origin.CurrNodeId
+	item.ApplyUserId = origin.ApplyUserId
+	item.ApplyUserName = origin.ApplyUserName
+	item.ApproveRemark = origin.ApproveRemark
+	item.ApproveTime = utils.TimeTransferString(utils.FormatDateTime, origin.ApproveTime)
+	item.HandleTime = utils.TimeTransferString(utils.FormatDateTime, origin.HandleTime)
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+// ReportApproveListReq 审批列表请求体
+type ReportApproveListReq struct {
+	PageSize         int    `form:"PageSize"`
+	CurrentIndex     int    `form:"CurrentIndex"`
+	ListType         int    `form:"ListType" description:"列表类型:1-待处理;2-已处理;3-我发起的"`
+	ReportType       int    `form:"ReportType" description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ClassifyFirstId  int    `form:"ClassifyFirstId" description:"一级分类ID"`
+	ClassifySecondId int    `form:"ClassifySecondId" description:"二级分类ID"`
+	Keyword          string `form:"Keyword" description:"关键词:报告标题"`
+	ApproveState     int    `form:"ApproveState" description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	TimeType         int    `form:"TimeType" description:"时间类型:1-提交时间;2-处理时间;3-审批时间"`
+	StartTime        string `form:"StartTime" description:"开始时间"`
+	EndTime          string `form:"EndTime" description:"结束时间"`
+	SortField        int    `form:"SortField" description:"排序字段:1-提交时间;2-处理时间;3-审批时间"`
+	SortRule         int    `form:"SortRule" description:"排序方式: 1-正序; 2-倒序(默认)"`
+}
+
+// ReportApproveListResp 审批列表响应体
+type ReportApproveListResp struct {
+	List   []*ReportApproveItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type ReportApproveItemOrm struct {
+	ReportApproveId       int       `description:"审批ID"`
+	ReportApproveRecordId int       `description:"审批记录ID"`
+	ReportType            int       `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ReportId              int       `description:"报告ID"`
+	ReportTitle           string    `description:"报告标题"`
+	ClassifyFirstId       int       `description:"一级分类ID"`
+	ClassifySecondId      int       `description:"二级分类ID"`
+	State                 int       `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	RecordState           int       `description:"审批记录状态:1-待审批;2-已通过;3-已驳回"`
+	FlowId                int       `description:"审批流ID"`
+	FlowVersion           int       `description:"审批流版本"`
+	StartNodeId           int       `description:"开始节点ID"`
+	CurrNodeId            int       `description:"当前节点ID"`
+	ApplyUserId           int       `description:"申请人ID"`
+	ApplyUserName         string    `description:"申请人姓名"`
+	ApproveRemark         string    `description:"审批备注"`
+	ApproveTime           time.Time `description:"审批时间"`
+	HandleTime            time.Time `description:"处理时间"`
+	CreateTime            time.Time `description:"创建时间"`
+	ModifyTime            time.Time `description:"修改时间"`
+}
+
+// GetApprovingReportApproveCount 获取待处理的审批分页列表总数
+func GetApprovingReportApproveCount(cond string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	base := fmt.Sprintf(`SELECT a.report_approve_record_id
+		FROM report_approve_record AS a
+		JOIN report_approve AS b ON a.report_approve_id = b.report_approve_id
+		WHERE 1 = 1 %s`, cond)
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM (%s) t`, base)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetApprovingReportApprovePageList 获取待处理的审批列表-分页
+func GetApprovingReportApprovePageList(cond string, pars []interface{}, orderRule string, startSize, pageSize int) (items []*ReportApproveItemOrm, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := `ORDER BY a.create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT a.report_approve_record_id, a.state AS record_state, b.*
+		FROM report_approve_record AS a
+		JOIN report_approve AS b ON a.report_approve_id = b.report_approve_id
+		WHERE 1 = 1 %s %s
+		LIMIT ?,?`, cond, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// GetApprovedReportApproveCount 获取已处理的审批分页列表总数
+func GetApprovedReportApproveCount(cond string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	base := fmt.Sprintf(`SELECT a.report_approve_record_id
+		FROM report_approve_record AS a
+		JOIN report_approve AS b ON a.report_approve_id = b.report_approve_id
+		WHERE 1 = 1 %s`, cond)
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM (%s) t`, base)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetApprovedReportApprovePageList 获取已处理的审批列表-分页
+func GetApprovedReportApprovePageList(cond string, pars []interface{}, orderRule string, startSize, pageSize int) (items []*ReportApproveItemOrm, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := `ORDER BY a.create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT a.report_approve_record_id, a.state AS record_state, a.approve_time AS handle_time, b.*
+		FROM report_approve_record AS a
+		JOIN report_approve AS b ON a.report_approve_id = b.report_approve_id
+		WHERE 1 = 1 %s %s
+		LIMIT ?,?`, cond, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// GetApplyReportApproveCount 获取我发起的审批分页列表总数
+func GetApplyReportApproveCount(cond string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	base := fmt.Sprintf(`SELECT a.* FROM report_approve AS a WHERE 1 = 1 %s`, cond)
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM (%s) t`, base)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetApplyReportApprovePageList 获取我发起的审批列表-分页
+func GetApplyReportApprovePageList(cond string, pars []interface{}, orderRule string, startSize, pageSize int) (items []*ReportApproveItemOrm, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := `ORDER BY a.create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT a.* FROM report_approve AS a WHERE 1 = 1 %s %s LIMIT ?,?`, cond, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// ReportApproveDetail 审批详情信息
+type ReportApproveDetail struct {
+	Report           *ReportApproveDetailReport  `description:"报告信息"`
+	Approve          *ReportApproveDetailItem    `description:"审批信息"`
+	ApproveFlowNodes []*ReportApproveDetailNodes `description:"审批节点信息"`
+	//ApproveFlow      *ReportApproveFlowItem      `description:"审批流信息"`
+}
+
+// ReportApproveDetailItem 审批详情-审批信息
+type ReportApproveDetailItem struct {
+	ReportApproveId int    `description:"审批ID"`
+	State           int    `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	FlowId          int    `description:"审批流ID"`
+	FlowVersion     int    `description:"审批流版本"`
+	StartNodeId     int    `description:"开始节点ID"`
+	CurrNodeId      int    `description:"当前节点ID"`
+	ApplyUserId     int    `description:"申请人ID"`
+	ApplyUserName   string `description:"申请人姓名"`
+	ApproveTime     string `description:"审批时间"`
+	CreateTime      string `description:"创建时间"`
+	ModifyTime      string `description:"修改时间"`
+}
+
+// ReportApproveDetailReport 审批详情-报告信息
+type ReportApproveDetailReport struct {
+	ReportType     int    `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ReportId       int    `description:"报告ID"`
+	ReportTitle    string `description:"报告标题"`
+	ReportClassify string `description:"报告分类"`
+	//ClassifyFirstId  int    `description:"一级分类ID"`
+	//ClassifySecondId int    `description:"二级分类ID"`
+	//Content          string `description:"报告内容"`
+}
+
+// CreateApproveAndRecord 新增审批和记录
+func (m *ReportApprove) CreateApproveAndRecord(approveItem *ReportApprove, recordItems []*ReportApproveRecord) (err error) {
+	if approveItem == nil {
+		err = fmt.Errorf("approve is nil")
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	tx, e := o.Begin()
+	if e != nil {
+		err = fmt.Errorf("orm begin err: %s", e.Error())
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	lastId, e := tx.Insert(approveItem)
+	if e != nil {
+		err = fmt.Errorf("insert approve err: %s", e.Error())
+		return
+	}
+	approveItem.ReportApproveId = int(lastId)
+
+	if len(recordItems) > 0 {
+		for _, v := range recordItems {
+			v.ReportApproveId = approveItem.ReportApproveId
+		}
+		_, e = tx.InsertMulti(len(recordItems), recordItems)
+		if e != nil {
+			err = fmt.Errorf("insert records err: %s", e.Error())
+			return
+		}
+	}
+	return
+}
+
+// ReportApprovePassReq 审批通过请求体
+type ReportApprovePassReq struct {
+	ReportApproveId int `description:"审批ID"`
+}
+
+// ReportApproveRefuseReq 审批驳回请求体
+type ReportApproveRefuseReq struct {
+	ReportApproveId int    `description:"审批ID"`
+	ApproveRemark   string `description:"驳回理由"`
+}
+
+// ReportApproveCancelReq 撤销审批请求体
+type ReportApproveCancelReq struct {
+	ReportApproveId int `description:"审批ID"`
+}
+
+// ReportApproveCheckApproveOpenReq 校验分类是否打开审批请求体
+type ReportApproveCheckApproveOpenReq struct {
+	ReportType       int `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ClassifyFirstId  int `description:"一级分类ID"`
+	ClassifySecondId int `description:"二级分类ID"`
+}

+ 387 - 0
models/report_approve/report_approve_flow.go

@@ -0,0 +1,387 @@
+package report_approve
+
+import (
+	"eta/eta_mobile/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+// ReportApproveFlow 报告审批流表
+type ReportApproveFlow struct {
+	ReportApproveFlowId int       `orm:"column(report_approve_flow_id);pk" description:"审批流ID"`
+	FlowName            string    `description:"审批流名称"`
+	ReportType          int       `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ClassifyFirstId     int       `description:"一级分类ID"`
+	ClassifySecondId    int       `description:"二级分类ID"`
+	CurrVersion         int       `description:"当前版本号"`
+	CreateTime          time.Time `description:"创建时间"`
+	ModifyTime          time.Time `description:"修改时间"`
+}
+
+var ReportApproveFlowCols = struct {
+	ReportApproveFlowId string
+	FlowName            string
+	ReportType          string
+	ClassifyFirstId     string
+	ClassifySecondId    string
+	CurrVersion         string
+	CreateTime          string
+	ModifyTime          string
+}{
+	ReportApproveFlowId: "report_approve_flow_id",
+	FlowName:            "flow_name",
+	ReportType:          "report_type",
+	ClassifyFirstId:     "classify_first_id",
+	ClassifySecondId:    "classify_second_id",
+	CurrVersion:         "curr_version",
+	CreateTime:          "create_time",
+	ModifyTime:          "modify_time",
+}
+
+func (m *ReportApproveFlow) TableName() string {
+	return "report_approve_flow"
+}
+
+func (m *ReportApproveFlow) PrimaryId() string {
+	return ReportApproveFlowCols.ReportApproveFlowId
+}
+
+func (m *ReportApproveFlow) Create() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.ReportApproveFlowId = int(id)
+	return
+}
+
+func (m *ReportApproveFlow) CreateMulti(items []*ReportApproveFlow) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *ReportApproveFlow) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *ReportApproveFlow) Del() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.ReportApproveFlowId).Exec()
+	return
+}
+
+func (m *ReportApproveFlow) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	_, err = o.Raw(sql, menuIds).Exec()
+	return
+}
+
+func (m *ReportApproveFlow) GetItemById(id int) (item *ReportApproveFlow, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveFlow) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *ReportApproveFlow, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveFlow) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *ReportApproveFlow) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*ReportApproveFlow, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *ReportApproveFlow) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*ReportApproveFlow, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// ReportApproveFlowItem 报告审批流信息
+type ReportApproveFlowItem struct {
+	ReportApproveFlowId int    `description:"审批流ID"`
+	FlowName            string `description:"审批流名称"`
+	ReportType          int    `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ClassifyFirstId     int    `description:"一级分类ID"`
+	ClassifySecondId    int    `description:"二级分类ID"`
+	ReportClassify      string `description:"报告类型: XX研报/一级分类/二级分类"`
+	CreateTime          string `description:"创建时间"`
+	ModifyTime          string `description:"修改时间"`
+}
+
+// FormatReportApproveFlow2Item 格式化报告审批流
+func FormatReportApproveFlow2Item(origin *ReportApproveFlow) (item *ReportApproveFlowItem) {
+	item = new(ReportApproveFlowItem)
+	if origin == nil {
+		return
+	}
+	item.ReportApproveFlowId = origin.ReportApproveFlowId
+	item.FlowName = origin.FlowName
+	item.ReportType = origin.ReportType
+	item.ClassifyFirstId = origin.ClassifyFirstId
+	item.ClassifySecondId = origin.ClassifySecondId
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+// ReportApproveFlowAddReq 新增报告审批流请求体
+type ReportApproveFlowAddReq struct {
+	FlowName         string                     `description:"审批流名称"`
+	ReportType       int                        `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ClassifyFirstId  int                        `description:"一级分类ID"`
+	ClassifySecondId int                        `description:"二级分类ID"`
+	Nodes            []ReportApproveNodeSaveReq `description:"审批节点信息"`
+}
+
+// ReportApproveFlowEditReq 编辑报告审批流请求体
+type ReportApproveFlowEditReq struct {
+	ReportApproveFlowId int `description:"审批流ID"`
+	ReportApproveFlowAddReq
+}
+
+// ReportApproveFlowRemoveReq 删除报告审批流请求体
+type ReportApproveFlowRemoveReq struct {
+	ReportApproveFlowId int `description:"报告审批流ID"`
+}
+
+type ReportClassifyTreeItem struct {
+	ClassifyId   int                       `description:"分类ID"`
+	ClassifyName string                    `description:"分类名称"`
+	ParentId     int                       `description:"父级ID"`
+	HasFlow      bool                      `description:"是否已配置审批流"`
+	Children     []*ReportClassifyTreeItem `description:"子分类"`
+}
+
+// CreateFlowAndNodes 新增审批流和节点
+func (m *ReportApproveFlow) CreateFlowAndNodes(flowItem *ReportApproveFlow, nodeItems []*ReportApproveNode) (err error) {
+	if flowItem == nil {
+		err = fmt.Errorf("flow is nil")
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	tx, e := o.Begin()
+	if e != nil {
+		err = fmt.Errorf("orm begin err: %s", e.Error())
+		return
+	}
+	prevNodes := make([]*ReportApproveNode, 0)
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+		// 更新每个节点的下一个节点信息, 放在事务中会更新失败
+		if e = m.UpdateNextNodes(prevNodes); e != nil {
+			err = fmt.Errorf("UpdatePrevNodes err: %s", e.Error())
+			return
+		}
+	}()
+
+	lastId, e := tx.Insert(flowItem)
+	if e != nil {
+		err = fmt.Errorf("insert flow err: %s", e.Error())
+		return
+	}
+	flowItem.ReportApproveFlowId = int(lastId)
+
+	nodesLen := len(nodeItems)
+	if nodesLen == 0 {
+		return
+	}
+	prevId := 0
+	prevNode := new(ReportApproveNode)
+	for k, v := range nodeItems {
+		v.ReportApproveFlowId = flowItem.ReportApproveFlowId
+		v.PrevNodeId = prevId
+		id, e := tx.Insert(v)
+		if e != nil {
+			err = fmt.Errorf("insert node err: %s", e.Error())
+			return
+		}
+		v.ReportApproveNodeId = int(id)
+		prevId = v.ReportApproveNodeId
+
+		// 下一个节点
+		if prevNode != nil && k > 0 && k < nodesLen {
+			prevNode.NextNodeId = int(id)
+			prevNodes = append(prevNodes, prevNode)
+		}
+		prevNode = v
+	}
+	return
+}
+
+func (m *ReportApproveFlow) UpdateNextNodes(nodes []*ReportApproveNode) (err error) {
+	if len(nodes) == 0 {
+		return
+	}
+	updateCols := []string{"NextNodeId"}
+	for _, v := range nodes {
+		e := v.Update(updateCols)
+		if e != nil {
+			err = fmt.Errorf("prev node update err: %s", e.Error())
+			return
+		}
+	}
+	return
+}
+
+// UpdateFlowAndNodes 更新审批流和节点
+func (m *ReportApproveFlow) UpdateFlowAndNodes(flowItem *ReportApproveFlow, nodeItems []*ReportApproveNode) (err error) {
+	if flowItem == nil {
+		err = fmt.Errorf("flow is nil")
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	tx, e := o.Begin()
+	if e != nil {
+		err = fmt.Errorf("orm begin err: %s", e.Error())
+		return
+	}
+	prevNodes := make([]*ReportApproveNode, 0)
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+		// 更新每个节点的下一个节点信息, 放在事务中会更新失败
+		if e = m.UpdateNextNodes(prevNodes); e != nil {
+			err = fmt.Errorf("UpdatePrevNodes err: %s", e.Error())
+			return
+		}
+	}()
+
+	// 更新审批流
+	updateCols := []string{"FlowName", "ReportType", "ClassifyFirstId", "ClassifySecondId", "CurrVersion", "ModifyTime"}
+	if e = flowItem.Update(updateCols); e != nil {
+		err = fmt.Errorf("update flow err: %s", e.Error())
+		return
+	}
+
+	// 新增节点(旧版的节点不删除, 根据版本号区分)
+	nodesLen := len(nodeItems)
+	if nodesLen == 0 {
+		return
+	}
+	prevId := 0
+	prevNode := new(ReportApproveNode)
+	for k, v := range nodeItems {
+		v.ReportApproveFlowId = flowItem.ReportApproveFlowId
+		v.PrevNodeId = prevId
+		id, e := tx.Insert(v)
+		if e != nil {
+			err = fmt.Errorf("insert node err: %s", e.Error())
+			return
+		}
+		v.ReportApproveNodeId = int(id)
+		prevId = v.ReportApproveNodeId
+
+		// 下一个节点
+		if prevNode != nil && k > 0 && k < nodesLen {
+			prevNode.NextNodeId = int(id)
+			prevNodes = append(prevNodes, prevNode)
+		}
+		prevNode = v
+	}
+	return
+}
+
+// ReportApproveFlowDetailItem 报告审批流详情信息
+type ReportApproveFlowDetailItem struct {
+	ReportApproveFlowItem `description:"审批流信息"`
+	Nodes                 []*ReportApproveNodeItem `description:"节点信息"`
+}
+
+// FormatFlowAndNodesItem2Detail 格式化审批流详情
+func FormatFlowAndNodesItem2Detail(flowItem *ReportApproveFlow, nodeItems []*ReportApproveNode) (detail *ReportApproveFlowDetailItem, err error) {
+	if flowItem == nil {
+		return
+	}
+	detail = new(ReportApproveFlowDetailItem)
+	detail.ReportApproveFlowId = flowItem.ReportApproveFlowId
+	detail.FlowName = flowItem.FlowName
+	detail.ReportType = flowItem.ReportType
+	detail.ClassifyFirstId = flowItem.ClassifyFirstId
+	detail.ClassifySecondId = flowItem.ClassifySecondId
+	detail.CreateTime = utils.TimeTransferString(utils.FormatDateTime, flowItem.CreateTime)
+	detail.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, flowItem.ModifyTime)
+	detail.Nodes = make([]*ReportApproveNodeItem, 0)
+	for _, v := range nodeItems {
+		t, e := FormatReportApproveNode2Item(v)
+		if e != nil {
+			err = fmt.Errorf("format node err: %s", e.Error())
+			return
+		}
+		detail.Nodes = append(detail.Nodes, t)
+	}
+	return
+}
+
+// ReportApproveFlowListReq 审批流列表请求体
+type ReportApproveFlowListReq struct {
+	PageSize         int    `form:"PageSize"`
+	CurrentIndex     int    `form:"CurrentIndex"`
+	ReportType       int    `form:"ReportType" description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ClassifyFirstId  int    `form:"ClassifyFirstId" description:"一级分类ID"`
+	ClassifySecondId int    `form:"ClassifySecondId" description:"二级分类ID"`
+	Keyword          string `form:"Keyword" description:"关键词"`
+	SortRule         int    `form:"SortRule" description:"排序方式: 1-正序; 2-倒序(默认)"`
+}
+
+// ReportApproveFlowListResp 审批流列表响应体
+type ReportApproveFlowListResp struct {
+	List   []*ReportApproveFlowItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}

+ 204 - 0
models/report_approve/report_approve_message.go

@@ -0,0 +1,204 @@
+package report_approve
+
+import (
+	"eta/eta_mobile/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+// ReportApproveMessage 报告审批消息表
+type ReportApproveMessage struct {
+	Id              int       `orm:"column(id);pk"`
+	SendUserId      int       `description:"发送人ID"`
+	ReceiveUserId   int       `description:"接收者ID"`
+	Content         string    `description:"消息内容"`
+	Remark          string    `description:"备注信息"`
+	ReportApproveId int       `description:"审批ID"`
+	ApproveState    int       `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	IsRead          int       `description:"是否已读:0-未读;1-已读"`
+	CreateTime      time.Time `description:"创建时间"`
+	ModifyTime      time.Time `description:"修改时间"`
+}
+
+var ReportApproveMessageCols = struct {
+	Id              string
+	SendUserId      string
+	ReceiveUserId   string
+	Content         string
+	Remark          string
+	ReportApproveId string
+	ApproveState    string
+	IsRead          string
+	CreateTime      string
+	ModifyTime      string
+}{
+	Id:              "id",
+	SendUserId:      "send_user_id",
+	ReceiveUserId:   "receive_user_id",
+	Content:         "content",
+	Remark:          "remark",
+	ReportApproveId: "report_approve_id",
+	ApproveState:    "approve_state",
+	IsRead:          "is_read",
+	CreateTime:      "create_time",
+	ModifyTime:      "modify_time",
+}
+
+func (m *ReportApproveMessage) TableName() string {
+	return "report_approve_message"
+}
+
+func (m *ReportApproveMessage) PrimaryId() string {
+	return ReportApproveMessageCols.Id
+}
+
+func (m *ReportApproveMessage) Create() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.Id = int(id)
+	return
+}
+
+func (m *ReportApproveMessage) CreateMulti(items []*ReportApproveMessage) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *ReportApproveMessage) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *ReportApproveMessage) Del() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.Id).Exec()
+	return
+}
+
+func (m *ReportApproveMessage) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	_, err = o.Raw(sql, menuIds).Exec()
+	return
+}
+
+func (m *ReportApproveMessage) GetItemById(id int) (item *ReportApproveMessage, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveMessage) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *ReportApproveMessage, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveMessage) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *ReportApproveMessage) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*ReportApproveMessage, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *ReportApproveMessage) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*ReportApproveMessage, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// ReportApproveMessageItem 报告审批消息信息
+type ReportApproveMessageItem struct {
+	Id              int
+	SendUserId      int    `description:"发送人ID"`
+	ReceiveUserId   int    `description:"接收者ID"`
+	Content         string `description:"消息内容"`
+	Remark          string `description:"备注信息"`
+	ReportApproveId int    `description:"审批ID"`
+	ApproveState    int    `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	IsRead          int    `description:"是否已读:0-未读;1-已读"`
+	CreateTime      string `description:"创建时间"`
+	ModifyTime      string `description:"修改时间"`
+}
+
+// FormatReportApproveMessage2Item 格式化报告审批消息
+func FormatReportApproveMessage2Item(origin *ReportApproveMessage) (item *ReportApproveMessageItem) {
+	item = new(ReportApproveMessageItem)
+	if origin == nil {
+		return
+	}
+	item.Id = origin.Id
+	item.SendUserId = origin.SendUserId
+	item.ReceiveUserId = origin.ReceiveUserId
+	item.Content = origin.Content
+	item.Remark = origin.Remark
+	item.ReportApproveId = origin.ReportApproveId
+	item.ApproveState = origin.ApproveState
+	item.IsRead = origin.IsRead
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+// ReportApproveMessageListReq 审批消息列表请求参数
+type ReportApproveMessageListReq struct {
+	PageSize     int `form:"PageSize"`
+	CurrentIndex int `form:"CurrentIndex"`
+}
+
+// ReportApproveMessageListResp 审批消息列表响应体
+type ReportApproveMessageListResp struct {
+	List        []*ReportApproveMessageItem
+	Paging      *paging.PagingItem `description:"分页数据"`
+	UnreadTotal int                `description:"消息未读数"`
+}
+
+// ReportApproveMessageReadReq 审批消息已读请求体
+type ReportApproveMessageReadReq struct {
+	MessageId int `description:"审批消息ID"`
+}

+ 215 - 0
models/report_approve/report_approve_node.go

@@ -0,0 +1,215 @@
+package report_approve
+
+import (
+	"encoding/json"
+	"eta/eta_mobile/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// ReportApproveNode 报告审批节点表
+type ReportApproveNode struct {
+	ReportApproveNodeId int       `orm:"column(report_approve_node_id);pk" description:"报告审批节点ID"`
+	ReportApproveFlowId int       `description:"报告审批流ID"`
+	PrevNodeId          int       `description:"上一个节点ID(0为开始节点)"`
+	NextNodeId          int       `description:"下一个节点ID(0为结束节点)"`
+	NodeType            int       `description:"节点类型:0-审批;1-抄送"`
+	ApproveType         int       `description:"审批类型:1-依次审批;2-会签;3-或签"`
+	Users               string    `description:"审批人信息-JSON,user_type:user-用户;role-角色,user_id:用户/角色ID"`
+	CurrVersion         int       `description:"当前版本号"`
+	CreateTime          time.Time `description:"创建时间"`
+}
+
+var ReportApproveNodeCols = struct {
+	ReportApproveNodeId string
+	ReportApproveFlowId string
+	PrevNodeId          string
+	NextNodeId          string
+	NodeType            string
+	ApproveType         string
+	Users               string
+	CurrVersion         string
+	CreateTime          string
+}{
+	ReportApproveNodeId: "report_approve_node_id",
+	ReportApproveFlowId: "report_approve_flow_id",
+	PrevNodeId:          "prev_node_id",
+	NextNodeId:          "next_node_id",
+	NodeType:            "node_type",
+	ApproveType:         "approve_type",
+	Users:               "users",
+	CurrVersion:         "curr_version",
+	CreateTime:          "create_time",
+}
+
+func (m *ReportApproveNode) TableName() string {
+	return "report_approve_node"
+}
+
+func (m *ReportApproveNode) PrimaryId() string {
+	return "report_approve_node_id"
+}
+
+func (m *ReportApproveNode) Create() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.ReportApproveNodeId = int(id)
+	return
+}
+
+func (m *ReportApproveNode) CreateMulti(items []*ReportApproveNode) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *ReportApproveNode) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *ReportApproveNode) Del() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.ReportApproveNodeId).Exec()
+	return
+}
+
+func (m *ReportApproveNode) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	_, err = o.Raw(sql, menuIds).Exec()
+	return
+}
+
+func (m *ReportApproveNode) GetItemById(id int) (item *ReportApproveNode, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveNode) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *ReportApproveNode, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveNode) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *ReportApproveNode) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*ReportApproveNode, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC, report_approve_node_id ASC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *ReportApproveNode) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*ReportApproveNode, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// ReportApproveNodeSaveReq 报告审批节点保存请求体
+type ReportApproveNodeSaveReq struct {
+	ApproveType int                        `description:"审批类型:1-依次审批;2-会签;3-或签"`
+	Users       []ReportApproveNodeUserReq `description:"审批人信息"`
+}
+
+// ReportApproveNodeUserReq 报告审批节点用户请求体
+type ReportApproveNodeUserReq struct {
+	UserType string `description:"审批人类型: user-用户; role-角色"`
+	UserId   int    `description:"用户/角色ID"`
+	UserName string `description:"用户/角色姓名"`
+	Sort     int    `description:"排序"`
+}
+
+// ReportApproveNodeItem 报告审批节点详情信息
+type ReportApproveNodeItem struct {
+	ReportApproveNodeId int                         `description:"报告审批节点ID"`
+	ReportApproveFlowId int                         `description:"报告审批流ID"`
+	PrevNodeId          int                         `description:"上一个节点ID(0为开始节点)"`
+	NextNodeId          int                         `description:"下一个节点ID(0为结束节点)"`
+	NodeType            int                         `description:"节点类型:0-审批;1-抄送"`
+	ApproveType         int                         `description:"审批类型:1-依次审批;2-会签;3-或签"`
+	Users               []*ReportApproveNodeUserReq `description:"审批人信息"`
+}
+
+// ReportApproveDetailNodes 审批详情-节点信息
+type ReportApproveDetailNodes struct {
+	ReportApproveNodeId int                            `description:"报告审批节点ID"`
+	ReportApproveFlowId int                            `description:"报告审批流ID"`
+	PrevNodeId          int                            `description:"上一个节点ID(0为开始节点)"`
+	NextNodeId          int                            `description:"下一个节点ID(0为结束节点)"`
+	NodeType            int                            `description:"节点类型:0-审批;1-抄送"`
+	ApproveType         int                            `description:"审批类型:1-依次审批;2-会签;3-或签"`
+	Users               []*ReportApproveDetailNodeUser `description:"审批人信息"`
+}
+
+// ReportApproveDetailNodeUser 审批详情-节点用户信息
+type ReportApproveDetailNodeUser struct {
+	ReportApproveNodeUserReq
+	ApproveRecord *ReportApproveDetailNodeUserRecord `description:"用户审批记录"`
+}
+
+// FormatReportApproveNode2Item 格式化报告审批节点信息
+func FormatReportApproveNode2Item(origin *ReportApproveNode) (item *ReportApproveNodeItem, err error) {
+	if origin == nil {
+		return
+	}
+	item = new(ReportApproveNodeItem)
+	item.ReportApproveNodeId = origin.ReportApproveNodeId
+	item.ReportApproveFlowId = origin.ReportApproveFlowId
+	item.PrevNodeId = origin.PrevNodeId
+	item.NextNodeId = origin.NextNodeId
+	item.NodeType = origin.NodeType
+	item.ApproveType = origin.ApproveType
+	item.Users = make([]*ReportApproveNodeUserReq, 0)
+	if origin.Users != "" {
+		e := json.Unmarshal([]byte(origin.Users), &item.Users)
+		if e != nil {
+			err = fmt.Errorf("node users unmarshal err: %s", e.Error())
+			return
+		}
+	}
+	return
+}

+ 220 - 0
models/report_approve/report_approve_record.go

@@ -0,0 +1,220 @@
+package report_approve
+
+import (
+	"eta/eta_mobile/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// ReportApproveRecord 报告审批记录表
+type ReportApproveRecord struct {
+	ReportApproveRecordId int       `orm:"column(report_approve_record_id);pk" description:"审批记录ID"`
+	ReportApproveId       int       `description:"审批ID"`
+	State                 int       `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	NodeId                int       `description:"节点ID"`
+	NodeType              int       `description:"节点类型:0-审批;1-抄送"`
+	PrevNodeId            int       `description:"上级节点ID"`
+	NextNodeId            int       `description:"下级节点ID"`
+	ApproveType           int       `description:"审批类型:1-依次审批;2-会签;3-或签"`
+	ApproveUserId         int       `description:"审批人ID"`
+	ApproveUserName       string    `description:"审批人姓名"`
+	ApproveUserSort       int       `description:"依次审批:用户的审批顺序"`
+	ApproveRemark         string    `description:"审批备注(驳回备注等)"`
+	ApproveTime           time.Time `description:"审批时间"`
+	CreateTime            time.Time `description:"创建时间"`
+	ModifyTime            time.Time `description:"修改时间"`
+}
+
+var ReportApproveRecordCols = struct {
+	ReportApproveRecordId string
+	ReportApproveId       string
+	State                 string
+	NodeId                string
+	NodeType              string
+	PrevNodeId            string
+	NextNodeId            string
+	ApproveType           string
+	ApproveRemark         string
+	ApproveUserId         string
+	ApproveUserName       string
+	ApproveUserSort       string
+	ApproveTime           string
+	CreateTime            string
+	ModifyTime            string
+}{
+	ReportApproveRecordId: "report_approve_record_id",
+	ReportApproveId:       "report_approve_id",
+	State:                 "state",
+	NodeId:                "node_id",
+	NodeType:              "node_type",
+	PrevNodeId:            "prev_node_id",
+	NextNodeId:            "next_node_id",
+	ApproveType:           "approve_type",
+	ApproveUserId:         "approve_user_id",
+	ApproveUserName:       "approve_user_name",
+	ApproveUserSort:       "approve_user_sort",
+	ApproveRemark:         "approve_remark",
+	ApproveTime:           "approve_time",
+	CreateTime:            "create_time",
+	ModifyTime:            "modify_time",
+}
+
+func (m *ReportApproveRecord) TableName() string {
+	return "report_approve_record"
+}
+
+func (m *ReportApproveRecord) PrimaryId() string {
+	return ReportApproveRecordCols.ReportApproveRecordId
+}
+
+func (m *ReportApproveRecord) Create() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.ReportApproveRecordId = int(id)
+	return
+}
+
+func (m *ReportApproveRecord) CreateMulti(items []*ReportApproveRecord) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *ReportApproveRecord) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *ReportApproveRecord) Del() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.ReportApproveRecordId).Exec()
+	return
+}
+
+func (m *ReportApproveRecord) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	_, err = o.Raw(sql, menuIds).Exec()
+	return
+}
+
+func (m *ReportApproveRecord) GetItemById(id int) (item *ReportApproveRecord, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveRecord) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *ReportApproveRecord, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveRecord) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *ReportApproveRecord) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*ReportApproveRecord, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *ReportApproveRecord) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*ReportApproveRecord, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// ReportApproveRecordItem 报告审批记录信息
+type ReportApproveRecordItem struct {
+	ReportApproveRecordId int    `description:"审批记录ID"`
+	ReportApproveId       int    `description:"审批ID"`
+	State                 int    `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	NodeId                int    `description:"节点ID"`
+	NodeType              int    `description:"节点类型:0-审批;1-抄送"`
+	PrevNodeId            int    `description:"上级节点ID"`
+	NextNodeId            int    `description:"下级节点ID"`
+	ApproveType           int    `description:"审批类型:1-依次审批;2-会签;3-或签"`
+	ApproveUserId         int    `description:"审批人ID"`
+	ApproveUserName       string `description:"审批人姓名"`
+	ApproveUserSort       int    `description:"依次审批:用户的审批顺序"`
+	ApproveRemark         string `description:"审批备注(驳回备注等)"`
+	ApproveTime           string `description:"审批时间"`
+	CreateTime            string `description:"创建时间"`
+	ModifyTime            string `description:"修改时间"`
+}
+
+// FormatReportApproveRecord2Item 格式化报告审批记录
+func FormatReportApproveRecord2Item(origin *ReportApproveRecord) (item *ReportApproveRecordItem) {
+	item = new(ReportApproveRecordItem)
+	if origin == nil {
+		return
+	}
+	item.ReportApproveRecordId = origin.ReportApproveRecordId
+	item.ReportApproveId = origin.ReportApproveId
+	item.State = origin.State
+	item.NodeId = origin.NodeId
+	item.NodeType = origin.NodeType
+	item.PrevNodeId = origin.PrevNodeId
+	item.NextNodeId = origin.NextNodeId
+	item.ApproveType = origin.ApproveType
+	item.ApproveUserId = origin.ApproveUserId
+	item.ApproveUserName = origin.ApproveUserName
+	item.ApproveUserSort = origin.ApproveUserSort
+	item.ApproveRemark = origin.ApproveRemark
+	item.ApproveTime = utils.TimeTransferString(utils.FormatDateTime, origin.ApproveTime)
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+// ReportApproveDetailNodeUserRecord 审批详情-节点用户审批记录
+type ReportApproveDetailNodeUserRecord struct {
+	ReportApproveRecordId int    `description:"审批记录ID"`
+	State                 int    `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	ApproveUserId         int    `description:"审批人ID"`
+	ApproveUserName       string `description:"审批人姓名"`
+	ApproveRemark         string `description:"审批备注"`
+	ApproveTime           string `description:"审批时间"`
+}

+ 350 - 0
models/smart_report/smart_report.go

@@ -0,0 +1,350 @@
+package smart_report
+
+import (
+	"eta/eta_mobile/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"html"
+	"strings"
+	"time"
+)
+
+const (
+	SmartReportStateWaitPublish = 1
+	SmartReportStatePublished   = 2
+)
+
+// SmartReport 智能研报
+type SmartReport struct {
+	SmartReportId       int       `orm:"column(smart_report_id);pk" description:"智能研报ID"`
+	ReportCode          string    `description:"报告唯一编码"`
+	ClassifyIdFirst     int       `description:"一级分类ID"`
+	ClassifyNameFirst   string    `description:"一级分类名称"`
+	ClassifyIdSecond    int       `description:"二级分类ID"`
+	ClassifyNameSecond  string    `description:"二级分类名称"`
+	AddType             int       `description:"新增方式:1-新增报告;2-继承报告"`
+	Title               string    `description:"标题"`
+	Abstract            string    `description:"摘要"`
+	Author              string    `description:"作者"`
+	Frequency           string    `description:"频度"`
+	Stage               int       `description:"期数"`
+	Content             string    `description:"内容"`
+	ContentSub          string    `description:"内容前两个章节"`
+	ContentStruct       string    `description:"内容组件"`
+	VideoUrl            string    `description:"音频文件URL"`
+	VideoName           string    `description:"音频文件名称"`
+	VideoPlaySeconds    float64   `description:"音频播放时长"`
+	VideoSize           string    `description:"音频文件大小,单位M"`
+	AdminId             int       `description:"创建者ID"`
+	AdminRealName       string    `description:"创建者姓名"`
+	State               int       `description:"1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过"`
+	LastModifyAdminId   int       `description:"最后更新人ID"`
+	LastModifyAdminName string    `description:"最后更新人姓名"`
+	ContentModifyTime   time.Time `description:"内容更新时间"`
+	Pv                  int       `description:"pv"`
+	Uv                  int       `description:"uv"`
+	PublishTime         time.Time `description:"发布时间"`
+	PrePublishTime      time.Time `description:"预发布时间"`
+	PreMsgSend          int       `description:"定时发布后是否推送模版消息:0-否;1-是"`
+	MsgIsSend           int       `description:"消息是否已发送:0-否;1-是"`
+	MsgSendTime         time.Time `description:"模版消息发送时间"`
+	DetailImgUrl        string    `description:"报告详情长图地址"`
+	DetailPdfUrl        string    `description:"报告详情PDF地址"`
+	CreateTime          time.Time `description:"创建时间"`
+	ModifyTime          time.Time `description:"修改时间"`
+	ApproveTime         time.Time `description:"审批时间"`
+	ApproveId           int       `description:"审批ID"`
+}
+
+func (m *SmartReport) TableName() string {
+	return "smart_report"
+}
+
+func (m *SmartReport) PrimaryId() string {
+	return "smart_report_id"
+}
+
+func (m *SmartReport) Create() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.SmartReportId = int(id)
+	return
+}
+
+func (m *SmartReport) CreateMulti(items []*SmartReport) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *SmartReport) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *SmartReport) Del() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.SmartReportId).Exec()
+	return
+}
+
+func (m *SmartReport) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	_, err = o.Raw(sql, menuIds).Exec()
+	return
+}
+
+func (m *SmartReport) GetItemById(id int) (item *SmartReport, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *SmartReport) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *SmartReport, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *SmartReport) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *SmartReport) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*SmartReport, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *SmartReport) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*SmartReport, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	// 排序:1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过
+	order := `ORDER BY FIELD(state,3,1,4,5,6,2), modify_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func (m *SmartReport) GetMaxStageByClassifyId(classifyId int) (stage int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT MAX(stage) AS max_stage FROM %s WHERE classify_id_second = ?`, m.TableName())
+	err = o.Raw(sql, classifyId).QueryRow(&stage)
+	return
+}
+
+// SmartReportItem 智能研报信息
+type SmartReportItem struct {
+	SmartReportId       int     `description:"智能研报ID"`
+	ReportCode          string  `description:"报告唯一编码"`
+	ClassifyIdFirst     int     `description:"一级分类ID"`
+	ClassifyNameFirst   string  `description:"一级分类名称"`
+	ClassifyIdSecond    int     `description:"二级分类ID"`
+	ClassifyNameSecond  string  `description:"二级分类名称"`
+	AddType             int     `description:"新增方式:1-新增报告;2-继承报告"`
+	Title               string  `description:"标题"`
+	Abstract            string  `description:"摘要"`
+	Author              string  `description:"作者"`
+	Frequency           string  `description:"频度"`
+	Stage               int     `description:"期数"`
+	Content             string  `description:"内容"`
+	ContentSub          string  `description:"内容前两个章节"`
+	ContentStruct       string  `description:"内容组件"`
+	VideoUrl            string  `description:"音频文件URL"`
+	VideoName           string  `description:"音频文件名称"`
+	VideoPlaySeconds    float64 `description:"音频播放时长"`
+	VideoSize           string  `description:"音频文件大小,单位M"`
+	AdminId             int     `description:"创建者姓名"`
+	AdminRealName       string  `description:"创建者姓名"`
+	LastModifyAdminId   int     `description:"最后更新人ID"`
+	LastModifyAdminName string  `description:"最后更新人姓名"`
+	ContentModifyTime   string  `description:"内容更新时间"`
+	Pv                  int     `description:"pv"`
+	Uv                  int     `description:"uv"`
+	State               int     `description:"1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过"`
+	PublishTime         string  `description:"发布时间"`
+	PrePublishTime      string  `description:"预发布时间"`
+	MsgIsSend           int     `description:"消息是否已发送:0-否;1-是"`
+	MsgSendTime         string  `description:"模版消息发送时间"`
+	DetailImgUrl        string  `description:"报告详情长图地址"`
+	DetailPdfUrl        string  `description:"报告详情PDF地址"`
+	CreateTime          string  `description:"创建时间"`
+	ModifyTime          string  `description:"修改时间"`
+	ApproveTime         string  `description:"审批时间"`
+	CanEdit             bool    `description:"是否可编辑"`
+	Editor              string  `description:"当前编辑人"`
+}
+
+// FormatSmartReport2Item 格式化智能研报数据格式
+func FormatSmartReport2Item(origin *SmartReport) (item *SmartReportItem) {
+	item = new(SmartReportItem)
+	if origin == nil {
+		return
+	}
+	item.SmartReportId = origin.SmartReportId
+	item.ReportCode = origin.ReportCode
+	item.ClassifyIdFirst = origin.ClassifyIdFirst
+	item.ClassifyNameFirst = origin.ClassifyNameFirst
+	item.ClassifyIdSecond = origin.ClassifyIdSecond
+	item.ClassifyNameSecond = origin.ClassifyNameSecond
+	item.AddType = origin.AddType
+	item.Title = origin.Title
+	item.Abstract = origin.Abstract
+	item.Author = origin.Author
+	item.Frequency = origin.Frequency
+	item.Stage = origin.Stage
+	item.Content = html.UnescapeString(origin.Content)
+	item.ContentSub = html.UnescapeString(origin.ContentSub)
+	item.ContentStruct = html.UnescapeString(origin.ContentStruct)
+	item.VideoUrl = origin.VideoUrl
+	item.VideoName = origin.VideoName
+	item.VideoPlaySeconds = origin.VideoPlaySeconds
+	item.VideoSize = origin.VideoSize
+	item.AdminId = origin.AdminId
+	item.AdminRealName = origin.AdminRealName
+	item.LastModifyAdminId = origin.LastModifyAdminId
+	item.LastModifyAdminName = origin.LastModifyAdminName
+	item.ContentModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ContentModifyTime)
+	item.Pv = origin.Pv
+	item.Uv = origin.Uv
+	item.State = origin.State
+	item.PublishTime = utils.TimeTransferString(utils.FormatDateTime, origin.PublishTime)
+	item.PrePublishTime = utils.TimeTransferString(utils.FormatDateTime, origin.PrePublishTime)
+	item.MsgIsSend = origin.MsgIsSend
+	item.MsgSendTime = utils.TimeTransferString(utils.FormatDateTime, origin.MsgSendTime)
+	item.DetailImgUrl = origin.DetailImgUrl
+	item.DetailPdfUrl = origin.DetailPdfUrl
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	item.ApproveTime = utils.TimeTransferString(utils.FormatDateTime, origin.ApproveTime)
+	return
+}
+
+// SmartReportAddReq 新增智能研报请求体
+type SmartReportAddReq 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:"频度"`
+}
+
+// SmartReportEditReq 编辑智能研报请求体
+type SmartReportEditReq struct {
+	SmartReportAddReq
+	SmartReportId int    `description:"智能研报ID"`
+	Content       string `description:"内容"`
+	ContentStruct string `description:"内容结构"`
+}
+
+// SmartReportRemoveReq 删除智能研报请求体
+type SmartReportRemoveReq struct {
+	SmartReportId int `description:"智能研报ID"`
+}
+
+// SmartReportPublishReq 发布智能研报请求体
+type SmartReportPublishReq struct {
+	SmartReportId int `description:"智能研报ID"`
+	PublishState  int `description:"1-取消发布; 2-发布"`
+}
+
+// SmartReportPrePublishReq 预发布智能研报请求体
+type SmartReportPrePublishReq struct {
+	SmartReportId  int    `description:"智能研报ID"`
+	PrePublishTime string `description:"预发布时间"`
+	PreMsgSend     int    `description:"定时发布成功后是否立即推送模版消息:0否,1是"`
+}
+
+// SmartReportSaveContentReq 保存草稿请求体
+type SmartReportSaveContentReq struct {
+	SmartReportId int    `description:"智能研报ID"`
+	Content       string `description:"内容"`
+	ContentStruct string `description:"内容结构"`
+	NoChange      int    `description:"内容是否未改变:1:内容未改变"`
+}
+
+// SmartReportSaveContentResp 保存草稿响应体
+type SmartReportSaveContentResp struct {
+	SmartReportId int `description:"智能研报ID"`
+}
+
+// SmartReportSendMsgReq 消息推送请求体
+type SmartReportSendMsgReq struct {
+	SmartReportId int `description:"智能研报ID"`
+}
+
+// SmartReportMarkEditReq 标记编辑英文研报的请求数据
+type SmartReportMarkEditReq struct {
+	SmartReportId int `description:"智能研报ID"`
+	Status        int `description:"标记状态: 1-编辑中; 2-编辑完成"`
+}
+
+// SmartReportListResp 智能研报
+type SmartReportListResp struct {
+	List   []*SmartReportItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+// ElasticSmartReport 智能研报es
+type ElasticSmartReport struct {
+	SmartReportId      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:"二级分类名称"`
+	StageStr           string `description:"报告期数"`
+	Frequency          string `description:"频度"`
+}
+
+// Report2ImgQueueReq 报告详情生成长图队列请求体
+type Report2ImgQueueReq struct {
+	ReportType int    `description:"报告类型: 1-研报; 2-智能研报"`
+	ReportCode string `description:"报告唯一编码"`
+}

+ 17 - 0
models/system/sys_admin.go

@@ -1,6 +1,7 @@
 package system
 
 import (
+	"fmt"
 	"github.com/beego/beego/v2/client/orm"
 	"strings"
 	"time"
@@ -71,3 +72,19 @@ func GetAdminByGroupId(groupId int) (items []*AdminItem, err error) {
 	_, err = o.Raw(sql, groupId).QueryRows(&items)
 	return
 }
+
+// GetSysAdminList 获取admin列表
+func GetSysAdminList(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*Admin, err error) {
+	fields := "*"
+	if len(fieldArr) > 0 {
+		fields = strings.Join(fieldArr, ",")
+	}
+	order := `enabled DESC, last_updated_time DESC`
+	if orderRule != "" {
+		order = orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM admin WHERE 1=1 %s ORDER BY %s`, fields, condition, order)
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}

+ 45 - 9
routers/commentsRouter.go

@@ -1276,6 +1276,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers/english_report:EnglishReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers/english_report:EnglishReportController"],
+        beego.ControllerComments{
+            Method: "CancelApprove",
+            Router: `/approve/cancel`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers/english_report:EnglishReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers/english_report:EnglishReportController"],
+        beego.ControllerComments{
+            Method: "SubmitApprove",
+            Router: `/approve/submit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mobile/controllers/english_report:EnglishReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers/english_report:EnglishReportController"],
         beego.ControllerComments{
             Method: "Author",
@@ -1438,6 +1456,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers/report_approve:ReportApproveController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers/report_approve:ReportApproveController"],
+        beego.ControllerComments{
+            Method: "CheckApproveOpen",
+            Router: `/classify/check_open`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mobile/controllers/sandbox:SandboxController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers/sandbox:SandboxController"],
         beego.ControllerComments{
             Method: "ListByQuote",
@@ -1465,15 +1492,6 @@ func init() {
             Filters: nil,
             Params: nil})
 
-    beego.GlobalControllerRouter["eta/eta_mobile/controllers:BusinessConfController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:BusinessConfController"],
-        beego.ControllerComments{
-            Method: "Save",
-            Router: `/save`,
-            AllowHTTPMethods: []string{"post"},
-            MethodParams: param.Make(),
-            Filters: nil,
-            Params: nil})
-
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:BusinessConfOpenController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:BusinessConfOpenController"],
         beego.ControllerComments{
             Method: "CodeEncrypt",
@@ -2122,6 +2140,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "CancelApprove",
+            Router: `/approve/cancel`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "SubmitApprove",
+            Router: `/approve/submit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
         beego.ControllerComments{
             Method: "Author",

+ 6 - 0
routers/router.go

@@ -16,6 +16,7 @@ import (
 	"eta/eta_mobile/controllers/data_manage/line_equation"
 	"eta/eta_mobile/controllers/data_manage/line_feature"
 	"eta/eta_mobile/controllers/english_report"
+	"eta/eta_mobile/controllers/report_approve"
 	"eta/eta_mobile/controllers/sandbox"
 	"eta/eta_mobile/controllers/semantic_analysis"
 
@@ -185,6 +186,11 @@ func init() {
 				&cross_variety.TagController{},
 			),
 		),
+		web.NSNamespace("/report_approve",
+			web.NSInclude(
+				&report_approve.ReportApproveController{},
+			),
+		),
 	)
 	web.AddNamespace(ns)
 }

+ 57 - 0
services/elastic.go

@@ -3,6 +3,7 @@ package services
 import (
 	"context"
 	"eta/eta_mobile/models"
+	"eta/eta_mobile/models/smart_report"
 	"eta/eta_mobile/services/alarm_msg"
 	"eta/eta_mobile/utils"
 	"fmt"
@@ -134,3 +135,59 @@ func EsAddOrEditEnglishReport(indexName, docId string, item *models.ElasticEngli
 	}
 	return
 }
+
+// EsAddOrEditSmartReport 新增编辑es智能研报
+func EsAddOrEditSmartReport(indexName, docId string, item *smart_report.ElasticSmartReport) (err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("EsAddOrEditSmartReport Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+	// docId为报告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{}{
+			"SmartReportId":      item.SmartReportId,
+			"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,
+			"StageStr":           item.StageStr,
+			"Frequency":          item.Frequency,
+		}).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
+}

+ 864 - 0
services/report_approve.go

@@ -0,0 +1,864 @@
+package services
+
+import (
+	"eta/eta_mobile/models"
+	"eta/eta_mobile/models/report_approve"
+	"eta/eta_mobile/models/smart_report"
+	"eta/eta_mobile/utils"
+	"fmt"
+	"sort"
+	"time"
+)
+
+// GetReportClassifyTreeRecursive 递归获取报告分类树
+func GetReportClassifyTreeRecursive(list []*models.Classify, parentId int) []*report_approve.ReportClassifyTreeItem {
+	res := make([]*report_approve.ReportClassifyTreeItem, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			t := new(report_approve.ReportClassifyTreeItem)
+			t.ClassifyId = v.Id
+			t.ClassifyName = v.ClassifyName
+			t.ParentId = v.ParentId
+			t.Children = GetReportClassifyTreeRecursive(list, v.Id)
+			res = append(res, t)
+		}
+	}
+	return res
+}
+
+// CheckReportApproveFlowChange 校验是否可变更分类
+func CheckReportApproveFlowChange(reportType, classifyFirstId, classifySecondId int) (ok bool, err error) {
+	var count int
+	cond := ` AND classify_id_first = ? AND classify_id_second = ? AND state = ?`
+	pars := make([]interface{}, 0)
+	pars = append(pars, classifyFirstId, classifySecondId, models.ReportStateWaitApprove)
+	switch reportType {
+	case report_approve.FlowReportTypeChinese:
+		ct, e := models.GetReportListCount(cond, pars, "")
+		if e != nil {
+			err = fmt.Errorf("GetReportListCount err: %s", e.Error())
+			return
+		}
+		count = ct
+	case report_approve.FlowReportTypeEnglish:
+		ct, e := models.GetEnglishReportListCount(cond, pars, "")
+		if e != nil {
+			err = fmt.Errorf("GetEnglishReportListCount err: %s", e.Error())
+			return
+		}
+		count = ct
+	case report_approve.FlowReportTypeSmart:
+		ob := new(smart_report.SmartReport)
+		ct, e := ob.GetCountByCondition(cond, pars)
+		if e != nil {
+			err = fmt.Errorf("SmartReport GetCountByCondition err: %s", e.Error())
+			return
+		}
+		count = ct
+	default:
+		err = fmt.Errorf("报告类型有误")
+		return
+	}
+	if count <= 0 {
+		ok = true
+	}
+	return
+}
+
+// CheckReportOpenApprove 校验报告是否开启了审批流
+func CheckReportOpenApprove(reportType, firstId, secondId int) (opening bool, err error) {
+	// 获取审批配置
+	confMap, e := models.GetBusinessConf()
+	if e != nil {
+		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
+		return
+	}
+	openMap := map[string]bool{"false": false, "true": true}
+	openApprove := openMap[confMap[models.BusinessConfIsReportApprove]]
+
+	// 查询对应分类是否有审批流
+	flowOb := new(report_approve.ReportApproveFlow)
+	flowCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId)
+	flowPars := make([]interface{}, 0)
+	flowPars = append(flowPars, reportType, firstId, secondId)
+	flowItem, e := flowOb.GetItemByCondition(flowCond, flowPars, "")
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		err = fmt.Errorf("ApproveFlow GetItemByCondition err: %s", e.Error())
+		return
+	}
+
+	// 开启审批/有审批流
+	if openApprove && (flowItem != nil || confMap[models.BusinessConfReportApproveType] == models.BusinessConfReportApproveTypeOther) {
+		opening = true
+		return
+	}
+	return
+}
+
+// CheckReportCurrState 校验报告当前应有的状态
+func CheckReportCurrState(reportType, firstId, secondId, operate int) (state int, err error) {
+	// 获取审批配置
+	confMap, e := models.GetBusinessConf()
+	if e != nil {
+		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
+		return
+	}
+	openMap := map[string]bool{"false": false, "true": true}
+	openApprove := openMap[confMap[models.BusinessConfIsReportApprove]]
+
+	// 查询对应分类是否有审批流
+	flowOb := new(report_approve.ReportApproveFlow)
+	flowCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId)
+	flowPars := make([]interface{}, 0)
+	flowPars = append(flowPars, reportType, firstId, secondId)
+	flowItem, e := flowOb.GetItemByCondition(flowCond, flowPars, "")
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		err = fmt.Errorf("ApproveFlow GetItemByCondition err: %s", e.Error())
+		return
+	}
+
+	// 开启审批/有审批流
+	if openApprove && (flowItem != nil || confMap[models.BusinessConfReportApproveType] == models.BusinessConfReportApproveTypeOther) {
+		// 操作为无审批的操作时, 会转为有审批的初始状态-待提交
+		stateMap := map[int]int{
+			models.ReportOperateAdd:           models.ReportStateWaitSubmit,  // 新增
+			models.ReportOperateEdit:          models.ReportStateWaitSubmit,  // 编辑
+			models.ReportOperatePublish:       models.ReportStateWaitSubmit,  // 发布
+			models.ReportOperateCancelPublish: models.ReportStateWaitSubmit,  // 取消发布
+			models.ReportOperateSubmitApprove: models.ReportStateWaitApprove, // 提审
+			models.ReportOperateCancelApprove: models.ReportStateWaitSubmit,  // 撤回
+		}
+		state = stateMap[operate]
+		return
+	}
+
+	// 关闭审批/分类无审批
+	// 操作为有审批的操作时, 会转为无审批的初始状态-未发布
+	stateMap := map[int]int{
+		models.ReportOperateAdd:           models.ReportStateUnpublished, // 新增
+		models.ReportOperateEdit:          models.ReportStateUnpublished, // 编辑
+		models.ReportOperatePublish:       models.ReportStatePublished,   // 发布
+		models.ReportOperateCancelPublish: models.ReportStateUnpublished, // 取消发布
+		models.ReportOperateSubmitApprove: models.ReportStateUnpublished, // 提审
+		models.ReportOperateCancelApprove: models.ReportStateUnpublished, // 撤回
+	}
+	state = stateMap[operate]
+	return
+}
+
+// SubmitReportApprove 提交审批
+func SubmitReportApprove(reportType, reportId int, reportTitle string, firstId, secondId int, sysAdminId int, sysAdminName string) (approveId int, err error) {
+	// 默认内部审批, 如果是走的第三方审批, 那么仅修改状态
+	confMap, e := models.GetBusinessConf()
+	if e != nil {
+		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
+		return
+	}
+	openMap := map[string]bool{"false": false, "true": true}
+	openApprove := openMap[confMap[models.BusinessConfIsReportApprove]]
+	if !openApprove {
+		err = fmt.Errorf("未开启审批")
+		return
+	}
+	confApproveType := confMap[models.BusinessConfReportApproveType]
+	if confApproveType == "" {
+		confApproveType = models.BusinessConfReportApproveTypeEta
+	}
+	if confApproveType == models.BusinessConfReportApproveTypeOther {
+		return
+	}
+
+	// 查询审批流
+	flowOb := new(report_approve.ReportApproveFlow)
+	flowCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId)
+	flowPars := make([]interface{}, 0)
+	flowPars = append(flowPars, reportType, firstId, secondId)
+	flowItem, e := flowOb.GetItemByCondition(flowCond, flowPars, "")
+	if e != nil {
+		err = fmt.Errorf("ApproveFlow GetItemByCondition err: %s", e.Error())
+		return
+	}
+
+	// 查询审批节点
+	nodeOb := new(report_approve.ReportApproveNode)
+	nodeCond := fmt.Sprintf(` AND %s = ? AND %s = ?`, report_approve.ReportApproveNodeCols.ReportApproveFlowId, report_approve.ReportApproveNodeCols.CurrVersion)
+	nodePars := make([]interface{}, 0)
+	nodePars = append(nodePars, flowItem.ReportApproveFlowId, flowItem.CurrVersion)
+	nodeItems, e := nodeOb.GetItemsByCondition(nodeCond, nodePars, []string{}, "")
+	if e != nil {
+		err = fmt.Errorf("ApproveNodes GetItemsByCondition err: %s", e.Error())
+		return
+	}
+	if len(nodeItems) == 0 {
+		err = fmt.Errorf("无审批节点")
+		return
+	}
+
+	// 取出首个节点
+	firstNodeItem := new(report_approve.ReportApproveNode)
+	for _, v := range nodeItems {
+		if v.PrevNodeId == 0 {
+			firstNodeItem = v
+			continue
+		}
+	}
+	if firstNodeItem == nil {
+		err = fmt.Errorf("首个审批节点有误")
+		return
+	}
+
+	// 审批信息
+	now := time.Now().Local()
+	newApprove := new(report_approve.ReportApprove)
+	newApprove.ReportType = reportType
+	newApprove.ReportId = reportId
+	newApprove.ReportTitle = reportTitle
+	newApprove.ClassifyFirstId = firstId
+	newApprove.ClassifySecondId = secondId
+	newApprove.State = report_approve.ReportApproveStateApproving
+	newApprove.FlowId = flowItem.ReportApproveFlowId
+	newApprove.FlowVersion = flowItem.CurrVersion
+	newApprove.StartNodeId = firstNodeItem.ReportApproveNodeId
+	newApprove.CurrNodeId = firstNodeItem.ReportApproveNodeId
+	newApprove.ApplyUserId = sysAdminId
+	newApprove.ApplyUserName = sysAdminName
+	newApprove.CreateTime = now
+	newApprove.ModifyTime = now
+	if e = newApprove.Create(); e != nil {
+		err = fmt.Errorf("生成审批信息失败, Err: %s", e.Error())
+		return
+	}
+	approveId = newApprove.ReportApproveId
+
+	// 生成节点审批记录
+	err = BuildNextNodeRecordAndMsg(firstNodeItem, newApprove.ReportApproveId, sysAdminId, sysAdminName)
+	return
+}
+
+// CancelReportApprove 撤回审批
+func CancelReportApprove(reportType, reportId, approveId, sysAdminId int, sysAdminName string) (err error) {
+	// 默认内部审批, 如果是走的第三方审批, 那么仅修改状态
+	confMap, e := models.GetBusinessConf()
+	if e != nil {
+		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
+		return
+	}
+	openMap := map[string]bool{"false": false, "true": true}
+	openApprove := openMap[confMap[models.BusinessConfIsReportApprove]]
+	if !openApprove {
+		//err = fmt.Errorf("未开启审批")
+		return
+	}
+	confApproveType := confMap[models.BusinessConfReportApproveType]
+	if confApproveType == "" {
+		confApproveType = models.BusinessConfReportApproveTypeEta
+	}
+	if confApproveType == models.BusinessConfReportApproveTypeOther {
+		return
+	}
+	if openApprove && confApproveType == models.BusinessConfReportApproveTypeOther {
+		e = updateReportApproveState(reportType, reportId, 0, models.ReportStateWaitSubmit)
+		if e != nil {
+			err = fmt.Errorf("更新报告审批撤回失败, Err: %s", e.Error())
+			return
+		}
+		return
+	}
+
+	// 修改审批信息状态
+	approveOb := new(report_approve.ReportApprove)
+	approveItem, e := approveOb.GetItemById(approveId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			return
+		}
+		err = fmt.Errorf("approve GetItemById err: %s", e.Error())
+		return
+	}
+	if approveItem.State == report_approve.ReportApproveStateCancel {
+		return
+	}
+	approveItem.State = report_approve.ReportApproveStateCancel
+	cols := []string{"State", "ModifyTime"}
+	if e = approveItem.Update(cols); e != nil {
+		err = fmt.Errorf("approve Update err: %s", e.Error())
+		return
+	}
+
+	// 修改报告状态
+	e = updateReportApproveState(reportType, reportId, 0, models.ReportStateWaitSubmit)
+	if e != nil {
+		err = fmt.Errorf("更新报告审批撤回失败, Err: %s", e.Error())
+		return
+	}
+
+	// 推送撤回消息
+	go func() {
+		recordOb := new(report_approve.ReportApproveRecord)
+		recordCond := fmt.Sprintf(` AND %s = ?`, report_approve.ReportApproveRecordCols.ReportApproveId)
+		recordPars := make([]interface{}, 0)
+		recordPars = append(recordPars, approveId)
+		recordItems, e := recordOb.GetItemsByCondition(recordCond, recordPars, []string{}, "")
+		if e != nil {
+			utils.FileLog.Info(fmt.Sprintf("approve record GetItemsByCondition err: %s", e.Error()))
+			return
+		}
+
+		messageOb := new(report_approve.ReportApproveMessage)
+		messages := make([]*report_approve.ReportApproveMessage, 0)
+		for _, v := range recordItems {
+			m := new(report_approve.ReportApproveMessage)
+			m.SendUserId = sysAdminId
+			m.ReceiveUserId = v.ApproveUserId
+			m.Content = fmt.Sprintf("%s提交的【研报审批】已撤回", sysAdminName)
+			m.ReportApproveId = approveId
+			m.ApproveState = report_approve.ReportApproveStateCancel
+			m.CreateTime = time.Now().Local()
+			m.ModifyTime = time.Now().Local()
+			messages = append(messages, m)
+		}
+		e = messageOb.CreateMulti(messages)
+		if e != nil {
+			utils.FileLog.Info(fmt.Sprintf("CancelReportApprove messages err: %s", e.Error()))
+			return
+		}
+	}()
+	return
+}
+
+// updateReportApproveState 更新报告审批状态
+func updateReportApproveState(reportType, reportId, approveId, state int) (err error) {
+	updateCols := []string{"ApproveId", "State", "ModifyTime"}
+	if reportType == report_approve.FlowReportTypeChinese {
+		reportOb := new(models.Report)
+		reportItem, e := reportOb.GetItemById(reportId)
+		// 报告可能会被删除, 这里查不到就忽略掉
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = fmt.Errorf("获取中文研报失败, Err: %s", e.Error())
+			return
+		}
+		if reportItem != nil {
+			reportItem.ApproveId = approveId
+			reportItem.State = state
+			reportItem.ModifyTime = time.Now().Local()
+			if state == models.ReportStatePass || state == models.ReportStateRefused {
+				updateCols = append(updateCols, "ApproveTime")
+				reportItem.ApproveTime = time.Now().Local()
+			}
+			if e = reportItem.UpdateReport(updateCols); e != nil {
+				err = fmt.Errorf("更新中文研报审批状态失败, Err: %s", e.Error())
+				return
+			}
+		}
+	}
+	if reportType == report_approve.FlowReportTypeEnglish {
+		reportItem, e := models.GetEnglishReportItemById(reportId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = fmt.Errorf("获取英文研报失败, Err: %s", e.Error())
+			return
+		}
+		if reportItem != nil {
+			reportItem.ApproveId = approveId
+			reportItem.State = state
+			reportItem.ModifyTime = time.Now().Local()
+			if state == models.ReportStatePass || state == models.ReportStateRefused {
+				updateCols = append(updateCols, "ApproveTime")
+				reportItem.ApproveTime = time.Now().Local()
+			}
+			if e = reportItem.UpdateReport(updateCols); e != nil {
+				err = fmt.Errorf("更新英文研报审批状态失败, Err: %s", e.Error())
+				return
+			}
+		}
+	}
+	if reportType == report_approve.FlowReportTypeSmart {
+		reportOb := new(smart_report.SmartReport)
+		reportItem, e := reportOb.GetItemById(reportId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = fmt.Errorf("获取智能研报失败, Err: %s", e.Error())
+			return
+		}
+		if reportItem != nil {
+			reportItem.ApproveId = approveId
+			reportItem.State = state
+			reportItem.ModifyTime = time.Now().Local()
+			if state == models.ReportStatePass || state == models.ReportStateRefused {
+				updateCols = append(updateCols, "ApproveTime")
+				reportItem.ApproveTime = time.Now().Local()
+			}
+			if e = reportItem.Update(updateCols); e != nil {
+				err = fmt.Errorf("更新智能研报审批状态失败, Err: %s", e.Error())
+				return
+			}
+		}
+	}
+	return
+}
+
+// PassReportApprove 通过审批
+func PassReportApprove(approveItem *report_approve.ReportApprove, recordItem *report_approve.ReportApproveRecord, sysAdminId int) (err error) {
+	if approveItem == nil {
+		err = fmt.Errorf("审批信息有误")
+		return
+	}
+	if recordItem == nil {
+		err = fmt.Errorf("审批记录有误")
+		return
+	}
+
+	// 查询审批流和审批流节点
+	flowOb := new(report_approve.ReportApproveFlow)
+	flowItem, e := flowOb.GetItemById(approveItem.FlowId)
+	if e != nil {
+		err = fmt.Errorf("获取审批流失败, Err: %s", e.Error())
+		return
+	}
+	nodeOb := new(report_approve.ReportApproveNode)
+	nodeCond := fmt.Sprintf(` AND %s = ? AND %s = ?`, report_approve.ReportApproveNodeCols.ReportApproveFlowId, report_approve.ReportApproveNodeCols.CurrVersion)
+	nodePars := make([]interface{}, 0)
+	nodePars = append(nodePars, flowItem.ReportApproveFlowId, flowItem.CurrVersion)
+	nodeItems, e := nodeOb.GetItemsByCondition(nodeCond, nodePars, []string{}, "")
+	if e != nil {
+		err = fmt.Errorf("ApproveNodes GetItemsByCondition err: %s", e.Error())
+		return
+	}
+	if len(nodeItems) == 0 {
+		err = fmt.Errorf("无审批节点")
+		return
+	}
+	nodeMap := make(map[int]*report_approve.ReportApproveNode)
+	for _, v := range nodeItems {
+		nodeMap[v.ReportApproveNodeId] = v
+	}
+
+	// 取出当前节点
+	currNodeItem := nodeMap[recordItem.NodeId]
+	if currNodeItem == nil {
+		err = fmt.Errorf("当前节点信息有误")
+		return
+	}
+	currNode, e := report_approve.FormatReportApproveNode2Item(currNodeItem)
+	if e != nil {
+		err = fmt.Errorf("当前节点信息有误, Err: %s", e.Error())
+		return
+	}
+	now := time.Now().Local()
+	recordItem.State = report_approve.ReportApproveStatePass
+	recordItem.ApproveTime = now
+	recordItem.ModifyTime = now
+	recordCols := []string{"State", "ApproveTime", "ModifyTime"}
+	lastApprove := false
+
+	// 依次审批
+	if currNode.ApproveType == report_approve.NodeApproveTypeRoll {
+		if e = recordItem.Update(recordCols); e != nil {
+			err = fmt.Errorf("更新审批记录状态失败, Err: %s", e.Error())
+			return
+		}
+
+		// 检查依次审批情况
+		sort.Slice(currNode.Users, func(k, j int) bool {
+			return currNode.Users[k].Sort < currNode.Users[j].Sort
+		})
+		userLen := len(currNode.Users)
+		//lastRoll := false
+		nextUser := new(report_approve.ReportApproveNodeUserReq) // 下一个审批人, 为nil则表示当前审批人即为最后
+		for k, v := range currNode.Users {
+			// 当前审批人
+			if v.UserId == sysAdminId && recordItem.ApproveUserSort == v.Sort {
+				//if k == (userLen - 1) {
+				//	lastRoll = true
+				//	break
+				//}
+				if (k + 1) < userLen {
+					nextUser = currNode.Users[k+1]
+				}
+			}
+		}
+		//if lastRoll && currNode.NextNodeId == 0 {
+		//	lastApprove = true
+		//}
+
+		// 当前节点下一个审批人, 生成下一个审批记录且return
+		if nextUser.UserId > 0 {
+			newRecord := new(report_approve.ReportApproveRecord)
+			newRecord.ReportApproveId = recordItem.ReportApproveId
+			newRecord.State = report_approve.ReportApproveStateApproving
+			newRecord.NodeId = currNode.ReportApproveNodeId
+			newRecord.PrevNodeId = currNode.PrevNodeId
+			newRecord.NextNodeId = currNode.NextNodeId
+			newRecord.ApproveType = currNode.ApproveType
+			newRecord.ApproveUserId = nextUser.UserId
+			newRecord.ApproveUserName = nextUser.UserName
+			newRecord.ApproveUserSort = nextUser.Sort
+			newRecord.CreateTime = now
+			newRecord.ModifyTime = now
+			if e = newRecord.Create(); e != nil {
+				err = fmt.Errorf("生成审批记录失败, Err: %s", e.Error())
+				return
+			}
+
+			// 推送审批消息
+			go func() {
+				messageItem := new(report_approve.ReportApproveMessage)
+				messageItem.SendUserId = approveItem.ApplyUserId
+				messageItem.ReceiveUserId = nextUser.UserId
+				messageItem.Content = "您有新的待办任务"
+				messageItem.Remark = fmt.Sprintf("%s提交的【研报审批】需要您审批,请及时处理", approveItem.ApplyUserName)
+				messageItem.ReportApproveId = approveItem.ReportApproveId
+				messageItem.ApproveState = report_approve.ReportApproveStateApproving
+				messageItem.CreateTime = now
+				messageItem.ModifyTime = now
+				if e = messageItem.Create(); e != nil {
+					utils.FileLog.Info(fmt.Sprintf("PassReportApprove message err: %s", e.Error()))
+					return
+				}
+			}()
+			return
+		}
+
+		// 更新审批当前节点并进入下一个节点
+		if currNode.NextNodeId > 0 {
+			nextNode := nodeMap[currNode.NextNodeId]
+			approveItem.CurrNodeId = currNode.NextNodeId
+			approveItem.ModifyTime = now
+			if e = approveItem.Update([]string{"CurrNodeId", "ModifyTime"}); e != nil {
+				err = fmt.Errorf("更新审批当前节点失败, Err: %s", e.Error())
+				return
+			}
+			err = BuildNextNodeRecordAndMsg(nextNode, approveItem.ReportApproveId, approveItem.ApplyUserId, approveItem.ApplyUserName)
+			return
+		} else {
+			// 最后一个节点
+			lastApprove = true
+		}
+	}
+
+	// 会签
+	if currNode.ApproveType == report_approve.NodeApproveTypeAll {
+		// 查询其他审批人是否已审批
+		otherOb := new(report_approve.ReportApproveRecord)
+		otherCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s <> ?`, report_approve.ReportApproveRecordCols.ReportApproveId, report_approve.ReportApproveRecordCols.NodeId, report_approve.ReportApproveRecordCols.ApproveUserId)
+		otherPars := make([]interface{}, 0)
+		otherPars = append(otherPars, approveItem.ReportApproveId, recordItem.NodeId, sysAdminId)
+		otherRecords, e := otherOb.GetItemsByCondition(otherCond, otherPars, []string{}, "")
+		if e != nil {
+			err = fmt.Errorf("获取节点审批记录失败, Err: %s", e.Error())
+			return
+		}
+		otherPass := true
+		for _, v := range otherRecords {
+			if v.State != report_approve.ReportApproveStatePass {
+				otherPass = false
+			}
+		}
+
+		// 其他人未审批, 仅更新当前审批记录
+		if e = recordItem.Update(recordCols); e != nil {
+			err = fmt.Errorf("更新审批记录状态失败, Err: %s", e.Error())
+			return
+		}
+
+		// 其他人已审批且为最后节点
+		if otherPass && currNode.NextNodeId == 0 {
+			lastApprove = true
+		}
+
+		// 其他人已审批且不为最后节点, 进入下一节点
+		if otherPass && currNode.NextNodeId > 0 {
+			nextNode := nodeMap[currNode.NextNodeId]
+			approveItem.CurrNodeId = currNode.NextNodeId
+			approveItem.ModifyTime = now
+			if e = approveItem.Update([]string{"CurrNodeId", "ModifyTime"}); e != nil {
+				err = fmt.Errorf("更新审批当前节点失败, Err: %s", e.Error())
+				return
+			}
+			err = BuildNextNodeRecordAndMsg(nextNode, approveItem.ReportApproveId, approveItem.ApplyUserId, approveItem.ApplyUserName)
+			return
+		}
+	}
+
+	// 或签
+	if currNode.ApproveType == report_approve.NodeApproveTypeAny {
+		if e = recordItem.Update(recordCols); e != nil {
+			err = fmt.Errorf("更新审批记录状态失败, Err: %s", e.Error())
+			return
+		}
+		if currNode.NextNodeId == 0 {
+			lastApprove = true
+		}
+		if currNode.NextNodeId > 0 {
+			nextNode := nodeMap[currNode.NextNodeId]
+			approveItem.CurrNodeId = currNode.NextNodeId
+			approveItem.ModifyTime = now
+			if e = approveItem.Update([]string{"CurrNodeId", "ModifyTime"}); e != nil {
+				err = fmt.Errorf("更新审批当前节点失败, Err: %s", e.Error())
+				return
+			}
+			err = BuildNextNodeRecordAndMsg(nextNode, approveItem.ReportApproveId, approveItem.ApplyUserId, approveItem.ApplyUserName)
+			return
+		}
+	}
+
+	// 最后一个审批, 更新审批记录、审批、报告状态、推送消息给申请人
+	if lastApprove {
+		if e = recordItem.Update(recordCols); e != nil {
+			err = fmt.Errorf("更新审批记录状态失败, Err: %s", e.Error())
+			return
+		}
+		approveItem.State = report_approve.ReportApproveStatePass
+		approveItem.ApproveTime = now
+		approveItem.ModifyTime = now
+		approveCols := []string{"State", "ApproveTime", "ModifyTime"}
+		if e = approveItem.Update(approveCols); e != nil {
+			err = fmt.Errorf("更新审批信息失败, Err: %s", e.Error())
+			return
+		}
+		if e = updateReportApproveState(approveItem.ReportType, approveItem.ReportId, approveItem.ReportApproveId, models.ReportStatePass); e != nil {
+			err = fmt.Errorf("更新报告审批状态失败, Err: %s", e.Error())
+			return
+		}
+
+		go func() {
+			messageItem := new(report_approve.ReportApproveMessage)
+			messageItem.SendUserId = sysAdminId
+			messageItem.ReceiveUserId = approveItem.ApplyUserId
+			messageItem.Content = "您提交的审批已通过"
+			messageItem.Remark = "您提交的【研报审批】已通过"
+			messageItem.ReportApproveId = approveItem.ReportApproveId
+			messageItem.ApproveState = report_approve.ReportApproveStatePass
+			messageItem.CreateTime = now
+			messageItem.ModifyTime = now
+			if e = messageItem.Create(); e != nil {
+				utils.FileLog.Info(fmt.Sprintf("PassReportApprove message err: %s", e.Error()))
+				return
+			}
+		}()
+
+		// 审批通过之后的处理
+		go func() {
+			if e = AfterReportApprovePass(approveItem.ReportType, approveItem.ReportId); e != nil {
+				utils.FileLog.Info(fmt.Sprintf("AfterReportApprovePass err: %s, ReportType: %d, ReportId: %d", e.Error(), approveItem.ReportType, approveItem.ReportId))
+				return
+			}
+		}()
+	}
+	return
+}
+
+// RefuseReportApprove 驳回审批
+func RefuseReportApprove(approveItem *report_approve.ReportApprove, recordItem *report_approve.ReportApproveRecord, approveRemark string, sysAdminId int) (err error) {
+	if approveItem == nil {
+		err = fmt.Errorf("审批信息有误")
+		return
+	}
+	if recordItem == nil {
+		err = fmt.Errorf("审批记录有误")
+		return
+	}
+
+	// 更新审批记录
+	now := time.Now().Local()
+	recordItem.State = report_approve.ReportApproveStateRefuse
+	recordItem.ApproveRemark = approveRemark
+	recordItem.ApproveTime = now
+	recordItem.ModifyTime = now
+	recordCols := []string{"State", "ApproveRemark", "ApproveTime", "ModifyTime"}
+	if e := recordItem.Update(recordCols); e != nil {
+		err = fmt.Errorf("更新审批记录状态失败, Err: %s", e.Error())
+		return
+	}
+
+	// 驳回-更新审批, 报告状态, 推送消息
+	approveItem.State = report_approve.ReportApproveStateRefuse
+	approveItem.ApproveRemark = approveRemark
+	approveItem.ApproveTime = now
+	approveItem.ModifyTime = now
+	approveCols := []string{"State", "ApproveRemark", "ApproveTime", "ModifyTime"}
+	if e := approveItem.Update(approveCols); e != nil {
+		err = fmt.Errorf("更新审批状态失败, Err: %s", e.Error())
+		return
+	}
+	if e := updateReportApproveState(approveItem.ReportType, approveItem.ReportId, approveItem.ReportApproveId, models.ReportStateRefused); e != nil {
+		err = fmt.Errorf("更新报告状态失败, Err: %s", e.Error())
+		return
+	}
+
+	// 推送驳回消息给申请人
+	go func() {
+		messageItem := new(report_approve.ReportApproveMessage)
+		messageItem.SendUserId = sysAdminId
+		messageItem.ReceiveUserId = approveItem.ApplyUserId
+		messageItem.Content = "您提交的审批被驳回"
+		messageItem.Remark = "您提交的【研报审批】已被驳回"
+		messageItem.ReportApproveId = approveItem.ReportApproveId
+		messageItem.ApproveState = report_approve.ReportApproveStateRefuse
+		messageItem.CreateTime = now
+		messageItem.ModifyTime = now
+		if e := messageItem.Create(); e != nil {
+			utils.FileLog.Info(fmt.Sprintf("ApproveReport message err: %s", e.Error()))
+			return
+		}
+	}()
+	return
+}
+
+// BuildNextNodeRecordAndMsg 生成下一个节点的审批记录并推送消息
+func BuildNextNodeRecordAndMsg(approveNodeItem *report_approve.ReportApproveNode, approveId, sysAdminId int, sysAdminName string) (err error) {
+	if approveNodeItem == nil {
+		err = fmt.Errorf("approve node nil")
+		return
+	}
+
+	// 根据节点审批方式生成审批记录
+	now := time.Now().Local()
+	approveNode, e := report_approve.FormatReportApproveNode2Item(approveNodeItem)
+	if e != nil {
+		err = fmt.Errorf("FormatReportApproveNode2Item err: %s", e.Error())
+		return
+	}
+	if len(approveNode.Users) == 0 {
+		err = fmt.Errorf("审批节点用户有误")
+		return
+	}
+	newRecords := make([]*report_approve.ReportApproveRecord, 0)
+	sort.Slice(approveNode.Users, func(k, j int) bool {
+		return approveNode.Users[k].Sort < approveNode.Users[j].Sort
+	})
+	for _, u := range approveNode.Users {
+		r := new(report_approve.ReportApproveRecord)
+		r.ReportApproveId = approveId
+		r.State = report_approve.ReportApproveStateApproving
+		r.NodeId = approveNode.ReportApproveNodeId
+		r.PrevNodeId = approveNode.PrevNodeId
+		r.NextNodeId = approveNode.NextNodeId
+		r.ApproveType = approveNode.ApproveType
+		r.ApproveUserId = u.UserId
+		r.ApproveUserName = u.UserName
+		r.ApproveUserSort = u.Sort
+		r.CreateTime = now
+		r.ModifyTime = now
+		newRecords = append(newRecords, r)
+		// 依次审批仅生成一条记录
+		if approveNode.ApproveType == report_approve.NodeApproveTypeRoll {
+			break
+		}
+	}
+
+	recordOb := new(report_approve.ReportApproveRecord)
+	if e = recordOb.CreateMulti(newRecords); e != nil {
+		err = fmt.Errorf("生成节点审批记录失败, Err: %s", e.Error())
+		return
+	}
+
+	// 推送审批消息
+	go func() {
+		messageOb := new(report_approve.ReportApproveMessage)
+		messages := make([]*report_approve.ReportApproveMessage, 0)
+		for _, v := range newRecords {
+			m := new(report_approve.ReportApproveMessage)
+			m.SendUserId = sysAdminId
+			m.ReceiveUserId = v.ApproveUserId
+			m.Content = "您有新的待办任务"
+			m.Remark = fmt.Sprintf("%s提交的【研报审批】需要您审批,请及时处理", sysAdminName)
+			m.ReportApproveId = approveId
+			m.ApproveState = report_approve.ReportApproveStateApproving
+			m.CreateTime = now
+			m.ModifyTime = now
+			messages = append(messages, m)
+		}
+		e = messageOb.CreateMulti(messages)
+		if e != nil {
+			utils.FileLog.Info(fmt.Sprintf("BuildNextNodeRecordAndMsg messages err: %s", e.Error()))
+			return
+		}
+	}()
+	return
+}
+
+// AfterReportApprovePass 报告审批通过后的处理
+func AfterReportApprovePass(reportType, reportId int) (err error) {
+	// 中文研报
+	if reportType == report_approve.FlowReportTypeChinese {
+		report, e := models.GetReportById(reportId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				return
+			}
+			err = fmt.Errorf("获取研报信息失败, Err: %s", e.Error())
+			return
+		}
+		_ = CreateVideo(report)
+		_ = UpdateReportEs(report.Id, models.ReportStatePublished)
+		return
+	}
+
+	// 英文研报
+	if reportType == report_approve.FlowReportTypeChinese {
+		_ = UpdateEnglishReportEs(reportId, models.ReportStatePublished)
+		return
+	}
+
+	// 智能研报
+	if reportType == report_approve.FlowReportTypeSmart {
+		ob := new(smart_report.SmartReport)
+		item, e := ob.GetItemById(reportId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				return
+			}
+			err = fmt.Errorf("获取智能研报信息失败, Err: %s", e.Error())
+			return
+		}
+
+		// 写入队列
+		var queue smart_report.Report2ImgQueueReq
+		queue.ReportType = 2
+		queue.ReportCode = item.ReportCode
+		_ = utils.Rc.LPush(utils.CACHE_CREATE_REPORT_IMGPDF_QUEUE, queue)
+
+		// 生成音频
+		if item.VideoUrl == "" {
+			SmartReportBuildVideoAndUpdate(item)
+		}
+
+		// ES更新报告
+		_ = SmartReportElasticUpsert(item.SmartReportId, models.ReportStatePublished)
+	}
+	return
+}
+
+// CheckCloseReportApproveConf 校验是否可以关闭报告审批
+func CheckCloseReportApproveConf() (yes bool, err error) {
+	// 查询待审批中的报告数量
+	count, e := models.GetReportStateCount(models.ReportStateWaitApprove)
+	if e != nil {
+		err = fmt.Errorf("查询审批中的报告数量失败, Err: %s", e.Error())
+		return
+	}
+	if count > 0 {
+		return
+	}
+	count, e = models.GetEnglishReportStateCount(models.ReportStateWaitApprove)
+	if e != nil {
+		err = fmt.Errorf("查询审批中的英文报告数量失败, Err: %s", e.Error())
+		return
+	}
+	if count > 0 {
+		return
+	}
+	smartOb := new(smart_report.SmartReport)
+	cond := ` AND state = ?`
+	pars := make([]interface{}, 0)
+	pars = append(pars, models.ReportStateWaitApprove)
+	count, e = smartOb.GetCountByCondition(cond, pars)
+	if e != nil {
+		err = fmt.Errorf("查询审批中的智能报告数量失败, Err: %s", e.Error())
+		return
+	}
+	if count > 0 {
+		return
+	}
+	yes = true
+	return
+}

+ 128 - 0
services/smart_report.go

@@ -0,0 +1,128 @@
+package services
+
+import (
+	"eta/eta_mobile/models/smart_report"
+	"eta/eta_mobile/services/alarm_msg"
+	"eta/eta_mobile/utils"
+	"fmt"
+	"html"
+	"strconv"
+	"time"
+)
+
+// SmartReportBuildVideoAndUpdate 生成音频
+func SmartReportBuildVideoAndUpdate(item *smart_report.SmartReport) {
+	if item == nil {
+		return
+	}
+	var err error
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("智能研报-音频生成, errMsg: %s", err.Error())
+			go alarm_msg.SendAlarmMsg(tips, 2)
+		}
+	}()
+
+	videoUrl, videoName, videoSize, videoPlaySeconds, e := CreateReportVideo(item.Title, item.Content, time.Now().Local().Format(utils.FormatDateTime))
+	if e != nil {
+		err = fmt.Errorf("create audio err: %s", e.Error())
+		return
+	}
+	item.VideoUrl = videoUrl
+	item.VideoName = videoName
+	item.VideoSize = videoSize
+	item.VideoPlaySeconds = videoPlaySeconds
+	item.ModifyTime = time.Now().Local()
+	cols := []string{"VideoUrl", "VideoName", "VideoSize", "VideoPlaySeconds", "ModifyTime"}
+	if e = item.Update(cols); e != nil {
+		err = fmt.Errorf("smart report update err: %s", e.Error())
+		return
+	}
+}
+
+// UpdateSmartReportEditing 更新研报当前更新状态
+// status 枚举值 1:编辑中,0:完成编辑, 2:只做查询
+//func UpdateSmartReportEditing(reportId, status, thisUserId int, thisUserName string, adminIdName map[int]string) (ret models.MarkReportResp, err error) {
+//	key := fmt.Sprint(utils.CACHE_SMART_REPORT_EDITING, reportId)
+//	ret.Status = 0
+//	ret.Msg = "无人编辑"
+//
+//	opUserId, e := utils.Rc.RedisInt(key)
+//	var opUser models.MarkReportItem
+//	var classifyNameFirst string
+//	if e != nil {
+//		opUserInfoStr, tErr := utils.Rc.RedisString(key)
+//		if tErr == nil {
+//			tErr = json.Unmarshal([]byte(opUserInfoStr), &opUser)
+//			if tErr == nil {
+//				opUserId = opUser.AdminId
+//			}
+//		}
+//	}
+//
+//	if opUserId > 0 && opUserId != thisUserId {
+//		editor := opUser.Editor
+//		if editor == "" {
+//			editor = adminIdName[opUserId]
+//		}
+//		ret.Status = 1
+//		ret.Msg = fmt.Sprintf("当前%s正在编辑报告", editor)
+//		ret.Editor = editor
+//		return
+//	}
+//
+//	if status == 1 {
+//		nowUser := &models.MarkReportItem{AdminId: thisUserId, Editor: thisUserName, ReportClassifyNameFirst: classifyNameFirst}
+//		bt, e := json.Marshal(nowUser)
+//		if e != nil {
+//			err = fmt.Errorf("格式化编辑者信息失败")
+//			return
+//		}
+//		if opUserId > 0 {
+//			utils.Rc.Do("SETEX", key, int64(180), string(bt)) //3分钟缓存
+//		} else {
+//			utils.Rc.SetNX(key, string(bt), time.Second*60*3) //3分钟缓存
+//		}
+//	} else if status == 0 {
+//		//清除编辑缓存
+//		_ = utils.Rc.Delete(key)
+//	}
+//	return
+//}
+
+// SmartReportElasticUpsert 新增/编辑报告es
+func SmartReportElasticUpsert(smartReportId int, state int) (err error) {
+	if smartReportId <= 0 {
+		return
+	}
+
+	reportOB := new(smart_report.SmartReport)
+	item, e := reportOB.GetItemById(smartReportId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			// 可能被删了就直接忽略掉
+			return
+		}
+		err = fmt.Errorf("获取报告失败, Err: %s", e.Error())
+		return
+	}
+
+	esReport := new(smart_report.ElasticSmartReport)
+	esReport.SmartReportId = item.SmartReportId
+	esReport.Title = item.Title
+	esReport.Abstract = item.Abstract
+	esReport.BodyContent = utils.TrimHtml(html.UnescapeString(item.Content))
+	esReport.PublishTime = item.PublishTime.Format(utils.FormatDateTime)
+	esReport.PublishState = state
+	esReport.Author = item.Author
+	esReport.ClassifyIdFirst = item.ClassifyIdFirst
+	esReport.ClassifyNameFirst = item.ClassifyNameFirst
+	esReport.ClassifyIdSecond = item.ClassifyIdSecond
+	esReport.ClassifyNameSecond = item.ClassifyNameSecond
+	esReport.StageStr = strconv.Itoa(item.Stage)
+	esReport.Frequency = item.Frequency
+	if err = EsAddOrEditSmartReport(utils.SmartReportIndexName, strconv.Itoa(item.SmartReportId), esReport); err != nil {
+		return
+	}
+	return
+}

+ 2 - 0
utils/config.go

@@ -83,6 +83,7 @@ var (
 	EsReportIndexName        string //研报ES索引
 	EsEnglishReportIndexName string //英文研报ES索引
 	MY_CHART_INDEX_NAME      string //研究图库(MY ETA)索引
+	SmartReportIndexName     string //智能研报ES索引
 )
 
 // 科大讯飞--语音合成
@@ -285,6 +286,7 @@ func init() {
 		MY_CHART_INDEX_NAME = config["my_chart_index_name"]
 		EsReportIndexName = config["es_report_index_name"]
 		EsEnglishReportIndexName = config["es_english_report_index_name"]
+		SmartReportIndexName = config["es_smart_report_index_name"]
 	}
 
 	// 微信相关

+ 4 - 2
utils/constants.go

@@ -205,6 +205,8 @@ const (
 	CACHE_KEY_MYSTEEL_REFRESH         = "mysteel_chemical:refresh"          //钢联化工刷新
 	CACHE_KEY_DAYNEW_REFRESH          = "admin:day_new:refresh"             //每日资讯拉取企业微信聊天记录
 	CACHE_KEY_DAYNEW_TRANSLATE        = "admin:day_new:translate"           //每日资讯中翻英
+
+	CACHE_CREATE_REPORT_IMGPDF_QUEUE = "eta_report:report_img_pdf_queue" // 生成报告长图PDF队列
 )
 
 // 模板消息推送类型
@@ -392,5 +394,5 @@ const (
 
 const (
 	WindDbWsd = "wsd"
-	ThsDs = "thsds"
-)
+	ThsDs     = "thsds"
+)